Skip to content

Scope enforcement is broken end-to-end (spans dpp-types, dpp-dal, dpp-vault) #23

Description

@LKSNDRTMLKV

Tracking issue linking three findings from the 32-crate audit that are individually filed, but together describe one systemic gap: the API-key scope system currently provides no real write-side enforcement anywhere in the engine.

The chain

  1. dpp-types (src/api_key.rs:62-71) — ApiKeyScope::from_scopes falls through to Admin on any unrecognized scope string, not just the documented empty-array back-compat case.
  2. dpp-dal (src/pg/repo_api_key.rs:27-35) — key_from_row maps a NULL/empty scopes column to Admin as well, on the auth hot path.
  3. dpp-vault (src/handlers/create.rs:48 and 6 sibling handlers) — ApiKeyScope::can_write(), the function that exists specifically to gate passport writes to Write/Admin scope, is never called anywhere in dpp-engine. None of the seven mutation handlers check auth.scope at all.

Why this matters as one issue, not three

Fixing any single link doesn't fix the chain. Even if from_scopes is corrected to fail closed to Read on an unrecognized value, dpp-vault still doesn't check scope at all — a genuinely Read-scoped key still gets full write access today. Conversely, wiring up can_write() in dpp-vault doesn't help if a corrupted/legacy row still resolves to Admin by default. All three need to land together (or in a sequence where the least-privilege default lands before enforcement is turned on, to avoid a transitional window where "unknown scope" both means "deny" and "no one checks it anyway").

Suggested acceptance criteria

  • ApiKeyScope::from_scopes fails closed (least privilege / explicit error) on any value it doesn't recognize, in both dpp-types and wherever dpp-dal constructs it from a DB row.
  • Every mutation handler in dpp-vault (create/update/publish/suspend/archive/eol/transfer) calls can_write() (or equivalent) before touching state, matching the pattern already used correctly in handlers/api_keys.rs, registry_identity.rs, and operator.rs.
  • A regression test that mints a Read-scoped key and asserts every mutation endpoint returns 403, so this can't silently regress again.

See the individual issues in dpp-types, dpp-dal, and dpp-vault for full file:line detail and failure scenarios.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingsecuritySecurity-relevant issue

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions