VC013A01:2021·CWE-269

Supabase Anon Key Used for Admin Operations

Supabase admin calls (auth.admin or rpc()) made from code that uses the anon key. Admin operations need the service_role key, and only on the server.

What this rule detects

VC013 fires in files that use Supabase with the anon key and never mention service_role, on calls to `supabase.auth.admin` or `supabase.rpc(`. Path traversal is a separate rule (VC117, plus VC093 for file downloads).

Vulnerable vs. safe code

Vulnerable
// Admin call made through a client built from the public anon key.
import { createClient } from "@supabase/supabase-js";

const supabase = createClient(
  process.env.NEXT_PUBLIC_SUPABASE_URL!,
  process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
);

export async function deleteAccount(id: string) {
  await supabase.auth.admin.deleteUser(id);
}
Safe
// lib/supabase-admin.ts — imported only by server code.
import "server-only";
import { createClient } from "@supabase/supabase-js";

const supabaseAdmin = createClient(
  process.env.SUPABASE_URL!,
  process.env.SUPABASE_SERVICE_ROLE_KEY!
);

export async function deleteAccount(id: string) {
  await supabaseAdmin.auth.admin.deleteUser(id);
}

About A01:2021 — Broken Access Control

Access-control bugs let users do things they shouldn't — view another user's data, modify records they don't own, or hit admin-only endpoints without admin rights. This class moved to #1 in the 2021 OWASP Top 10; OWASP reports that 94% of applications were tested for some form of broken access control, with an average incidence rate of 3.81%.

Impact: An attacker who finds an access-control bug typically gets a much bigger blast radius than other vulnerability classes. Examples we've seen in AI-generated code: a /api/user/[id] route that returns any user's profile, a delete endpoint that doesn't check ownership, an admin dashboard with no role check.

How to fix it: Check authentication AND authorization on every server-side route. Never rely on the client to enforce permissions. Default to deny. For per-resource checks, fetch the resource and verify the requesting user owns it before returning or modifying it.

Common patterns in this category:

  • Missing auth check on API routes (the resource is returned to anyone with the URL)
  • Authorization based on a parameter the client controls (e.g. trusting `?userId=` in the URL)
  • Direct object references where the resource ID isn't validated against the requesting user
  • Public access to administrative or internal endpoints
  • Privilege escalation via mass assignment (user POSTs `{role: 'admin'}` and the ORM accepts it)
  • State-changing routes without CSRF protection

Framework mapping

Findings from this rule map to the following framework controls. This is an informational mapping, not an audit, certification, or attestation.

SOC 2
CC6.1, CC6.3
ISO 27001
A.8.2, A.8.3
OWASP Top 10
A01:2021 — Broken Access Control
CWE
CWE-269

See the full compliance coverage page for how every rule maps to OWASP Top 10 and CWE, and which rules also carry informational SOC 2 and ISO 27001 cross-references.

VC013 is part of the full 223-rule set on paid plans (from $19/mo)

The free, no-signup scan (paste code, upload a file, or npx xploitscan@latest scan .) runs 30 core rules.

Scan Your Code →

Related rules in A01:2021