Skip to content

decision: how a datasource driver with no shipped config contract keeps credentials out of metadata (declaration vs. key-name heuristic) #21921

Closed

Description

@objectstack-fleet

Decision card for the open approach question on #21840 and its PR #21877. The withheld security detail stays withheld (RUNNER rule 2): this card names approaches and positions only.

The question

A datasource whose driver ships no config contract (a plugin-contributed driver) has a config that nothing validates. #21840 requires that credential material in that config never sits in metadata in clear text and is never served back.

PR #21877 meets this by guessing from key names and value shapes. A shared walk decides which keys and values look like credentials. The write door refuses what the walk finds, and the read door withholds it. Four rounds of independent security review each found new misses or false positives, and each round grew the rule:

  • arrays, header pairs and header lines;
  • connection-string forms;
  • PEM key material;
  • non-ASCII and confusable key names;
  • SQL placeholders and prose.

The PR now adds about 4,000 lines, against 1,230 in round 1. The rule file alone is 1,651 lines, and the rule is a public export (isCredentialShapedConfigKey), so every later correction is another accept-set narrowing. No datasource in this repository uses a contractless driver today.

How established systems do it

  • Declaration is the boundary.
    • Grafana data-source plugins split their config into jsonData and secureJsonData; the second part is encrypted and never returned to the browser.
    • Terraform provider schemas mark attributes Sensitive.
    • Kubernetes keeps credentials in Secret objects.
    • Salesforce has Named Credentials.
    • ObjectStack's own known drivers already work this way: their config contracts plus the bound secret (external.credentialsRef, ADR-0062 D3).
  • Key-name matching is a backstop only. Airflow's secrets masker and Sentry's data scrubbing mask keys such as password, token and api_key in logs and UIs. They are documented as best effort and are not the boundary.

Options

  • A — Declaration, and opaque by default (recommended).
    • With no config contract, the read door serves the key names but withholds every string value of config.
    • The write door keeps only a small, stable refusal of obvious inline credentials, or none, with the bound secret as the documented route.
    • A plugin driver that wants its config shown registers a config contract that marks its secret fields, the same way the shipped drivers do.
    • The heuristic walk is removed or reduced to a non-public backstop.
    • Cost: an administrator cannot read back the plain settings of a contractless datasource until its driver registers a contract. No datasource in this repository is affected.
  • B — Keep the key-name heuristic and continue until review converges. At least one more review round. The rule stays public, so each later fix is another breaking narrowing, and coverage is never complete by construction.
  • C — Land round 1 (the small heuristic) now and file the rest as follow-ups. Round 1 has known medium-severity misses.

Recommendation

A. It matches how established systems draw the line. It fails closed by construction instead of by enumeration, and it keeps the public surface small. B and C keep a boundary that depends on guessing.

Related: #21840, #21877, ADR-0015 §10, ADR-0062 D3, ADR-0100.

维护者速读

#21877 现在是靠“字段名像不像密钥”来判断插件驱动 config 里的凭据,复审一轮一轮补,已经到 4000 行,而且永远补不全。主流系统(Grafana、Terraform、Kubernetes、Salesforce)都是让驱动或插件声明哪些字段是密钥;按字段名匹配只用来遮盖日志,作为兜底。

  • A(推荐):没有声明的驱动,config 的值读取时一律隐藏,只露出键名;想正常显示,就让插件驱动注册配置约定。代码大幅变小,默认就是安全的。
  • B:继续补猜测规则。
  • C:先合第一版小规则,剩下的另开卡。

Generated by Claude Code

Activity

  1. objectstack-fleet commented on Oct 6, 2026

    @objectstack-fleet
    ContributorAuthor

    Path: an API a customer can call — external data; datasource admin lifecycle | integration-system.datasource-admin-lifecycle | P2

    Triage: graded — priority:p2 · area:access. needs-user-decision stays, and the card goes to the maintainer's decision box first: an in-flight PR is waiting on it

    Triage seat (objectstack-wide, seat post #6015) · session_01AavokzJ5DndAwitDXvKy4U · 2026-10-06T00:56Z. ⛔ Not a claim, ⛔ not a dispatch, ⛔ not a ruling.

    Triage: lands in the datasource credential handling that #21840 owns (PR #21877) ⇒ domain:spec as filed; rationale: the choice sets a security boundary's shape, so it belongs to the maintainer and triage does not rule it.


    Generated by Claude Code

  2. added
    area:accessPermissions that actually hold — RLS/FLS, sharing model, write-path guards
    on Oct 6, 2026
  3. objectstack-fleet commented on Oct 6, 2026

    @objectstack-fleet
    ContributorAuthor

    Ruled, recorded by the PM seat from the maintainer's answer in Claude Code session session_018zT8d8NpiQ1ExhuNd5TxY6, 2026-10-06, verbatim: 「插件驱动 是写代码开发,安全性应该由写代码的人控制,不应该我们盲猜」 (a plugin driver is code someone writes; its security is that author's to control, and the platform should not guess).

    • No key-name or value-shape heuristic decides what is a credential in a driver's config. The heuristic in PR fix(spec)!: credential-shaped datasource config values are refused at write and redacted on every read door for drivers with no shipped contract #21877 is not landed.
    • The plugin author declares. A plugin-contributed driver gets a supported way to register its config contract, marking which fields are secret, the same machinery the shipped drivers use (DRIVER_CONFIG_SCHEMAS, the bound secret external.credentialsRef, ADR-0062 D3). For a registered contract, the platform enforces it at the write door and the read door exactly as it does for the shipped drivers.
    • An unregistered driver keeps today's behaviour. Its config is not validated and not guessed at. The docs state that keeping credentials out of config, through the bound secret or a registered contract, is the plugin author's responsibility.

    Execution: #21840 is re-scoped to the registration path and the docs, and PR #21877 is closed without merging. This card is closed as decided.


    Generated by Claude Code

  4. objectstack-fleet commented on Oct 6, 2026

    @objectstack-fleet
    ContributorAuthor

    Correction to the ruling above, from the maintainer in the same session, 2026-10-06, verbatim: 「我觉得不需要协议,也不需要改动这么多代码」 and 「同意作废,文档补一句」.


    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:accessPermissions that actually hold — RLS/FLS, sharing model, write-path guardsdomain:specpriority:p2Medium: important, M3security

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions