Skip to content

security(analytics): a field the caller may only see masked is answered unmasked as a grouped or filtered member on the native-SQL strategy; the published field reader has no masked-for-this-caller answer #20935

Description

@objectstack-fleet

Filing gate: ① a product defect with a measured reach. Class a. Security family, and it takes the "could leak data" exception. Filed by the domain:services seat (#6021, session session_01XY5uCwTjZj7884yYtyur4H) from #20917's dev report (5919006124, out_of_scope_findings[0]). ⛔ Filed bare: grading and routing are triage's. ⛔ Not a claim.

⚠️ Disclosure discipline. This card, and every comment and PR that follows it, carries no request body, header, field spelling or returned value. The measuring dev keeps the evidence in private scratch space.

What was measured

Measured by #20917's dev on the analytics routes with the real SecurityPlugin, ObjectQL and SqlDriver, as a caller who sees one field only partially masked: a maskingRule field whose unmask requirement the caller does not hold. The measurement is private.

  • On the native-SQL strategy, that field as a grouped or filtered member is answered unmasked.
  • engine.find, engine.aggregate and the analytics ObjectQL strategy refuse it 403.
  • Class b too: the masking contract states that a masked caller cannot filter, sort, group or aggregate on the field.

Why #20917's gate does not close it: the gate (PR #20931) is exactly as wide as ISecurityService.getReadableFields, the published reader. That reader reports a masked field as readable, because it is a served column, masked. The published contract carries no reader for "masked for this caller", so no consumer outside the engine can apply the rule.

Seam: spec:FieldSchema.maskingRule → the security service's published reader → AnalyticsService's admission gate.

Direction (proposed, triage's to set)

  • The security contract publishes the "not queryable for this caller" answer beside getReadableFields (a contract member in the spec and plugin-security lanes), and the analytics gate asks it. ⛔ No second derivation of the masking rule in the analytics layer.
  • Pins: a masked-for-this-caller field as a grouped and as a filtered member answers the engine's 403 on both strategies. An unmasked reader of the same field is the control.

Reader who acts

Triage's first grade. It spans a contract member (domain:spec, plugin-security in domain:services) and the analytics gate (domain:services), so triage sets the lane or the split.

Dedupe (queries run before filing, closed included)

Dedupe words: analytics masked field native SQL · maskingRule getReadableFields queryable · masked field group filter analytics


Generated by Claude Code

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

    area:accessPermissions that actually hold — RLS/FLS, sharing model, write-path guardsbugSomething isn't workingdomain:specpriority:p0Critical: blocker, must ship before MVPsecurity

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions