Skip to content

Consent payload filter is default-open: unregistered keys ship content at usage #615

Description

@kcarnold

What

filterPayloadForConsent (frontend/src/consent.ts, mirrored server-side) keeps any payload key that isn't listed in KEY_MIN_LEVEL:

const min = KEY_MIN_LEVEL[key];
if (min && consentRank(min) > rank) continue;
kept[key] = value;

So the gate is an opt-in denylist. A content-bearing field ships in full at the usage default unless somebody remembered to register its name.

Why it matters

usage is DEFAULT_CONSENT_LEVEL — it's what nearly every user is on, and its label promises "No document text and no AI suggestions are stored." A new payload field named brief, excerpt, selection, snippet, or title would break that promise silently. Nothing would fail: no type error, no test, no runtime warning. The only defense today is a docstring in frontend/src/api/logging.ts asking call sites to "use names the consent gate recognizes," and LogEvent is declared [key: string]: any, so TypeScript can't help.

This is a privacy-policy-adjacent invariant defended by naming convention.

Suggested fix

Invert the default: classify every key explicitly, and treat unknown keys as the most restrictive level rather than the least.

  • Keep an explicit set of usage-safe keys (generationType, feature, features, selected, isOverall, source, responseLength, field, hasContent, tool, sharedDoc, scopes, error, code, found, attempts, durationMs, plus the envelope fields), and drop anything unrecognized above usage.
  • A dropped field is a lost metric; an unclassified field that ships is an incident. The asymmetry should be encoded in the default.
  • Optionally back it with types: give each event helper in api/logging.ts a declared payload type instead of [key: string]: any, so a new field has to be classified at the point it's added.
  • Add a test asserting an unregistered key does not survive at usage.

Both copies (frontend/src/consent.ts and backend/src/consent.ts) need the change, since the server re-applies the rules authoritatively.

Notes

Related to #503 (consent UI and open consent decisions), but distinct: that issue is about what we ask users, this one is about whether the code can keep the promise once they've answered.

Activity

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

    No labels
    No labels

    Type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions