Skip to content

[Security] [HIGH] Merchant impersonation accessible without super-admin check or merchant consent (admin) #62

Description

@cursor

Summary

Severity: HIGH
Source: Cursor vulnerability scan (2026-07-13)
Repository: apps/admin
Location: src/pages/overview/Overview.tsx
Verified: code-traced
Scan run: bc-8bc4c86c-6c11-4e73-8ef6-9eb4d2d770c4
Commit: 642e964aecca48aaa75ab160ada9281118a4ccc0

Description

The handleImpersonate function generates magic links via the admin-impersonate edge function for any merchant, but has no isSuperAdmin authorization check. Any admin with overview page access can impersonate any merchant on the platform. The database schema includes allow_staff_impersonation and staff_impersonation_consent_at fields on organizations, and an update_staff_impersonation_consent RPC exists, but these consent mechanisms are completely unused in the impersonation flow. Additionally, the redirect_to parameter sent to the edge function includes a http://localhost:5173/callback option that is visible in production builds with no import.meta.env.DEV guard.

Impact

Any admin with overview page access (the most commonly granted page) can perform full merchant account takeover — accessing merchant PII, initiating payouts, and changing merchant settings. The lack of merchant consent checking means merchants who have not opted in to staff impersonation can still be impersonated.

Attack path

  1. Attacker has a valid admin JWT with overview page access (no super-admin needed)
  2. Attacker clicks "Impersonate" on any merchant in the merchants list (or calls supabase.functions.invoke("admin-impersonate") directly)
  3. Edge function returns action_link magic link — no isSuperAdmin or allow_staff_impersonation consent check is performed client-side
  4. Attacker opens the magic link and gains a full merchant session on dashboard.lomi.africa

Evidence

  • src/pages/overview/Overview.tsx:402-446handleImpersonate sends target_merchant_id, target_email, redirect_to with no authorization check
  • src/pages/overview/Overview.tsx:1815-1830 — Impersonate button rendered for every merchant with no isSuperAdmin guard
  • src/pages/overview/Overview.tsx:1732-1759 — Localhost impersonation toggle visible in production UI
  • src/lib/utils/dashboard-url.ts:3-7http://localhost:5173/callback hardcoded as valid redirect target
  • src/lib/types/database.types.ts:5033,5044allow_staff_impersonation and staff_impersonation_consent_at fields exist but are never referenced in src/pages/overview/

Remediation

Gate the impersonation feature behind isSuperAdmin in the frontend. Server-side, the admin-impersonate edge function must independently verify the caller is a super-admin, check allow_staff_impersonation on the target organization, and strictly allowlist redirect_to to https://dashboard.lomi.africa/callback only. Remove the localhost callback toggle from production builds by guarding it with import.meta.env.DEV.

PoC

Video not feasible — exploitation requires an authenticated admin session on the live Supabase instance. Vulnerability confirmed through static code analysis showing absence of authorization checks and consent verification in the impersonation flow.

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

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions