Skip to main content
ClaudeWave
Skill403 repo starsupdated 4mo ago

convex-security-audit

The convex-security-audit Claude Code skill provides structured patterns for auditing Convex backend applications across five critical security domains: authorization logic using role-based access control, data access boundaries to restrict user visibility, action isolation for external API calls, rate limiting to prevent abuse, and sensitive operation protection. Use this skill when building or reviewing Convex applications to systematically validate authentication flows, enforce permission boundaries, isolate risky operations, implement usage controls, and safeguard privileged functions.

Install in Claude Code
Copy
git clone --depth 1 https://github.com/waynesutton/convexskills /tmp/convex-security-audit && cp -r /tmp/convex-security-audit/skills/convex-security-audit ~/.claude/skills/convex-security-audit
Then start a new Claude Code session; the skill loads automatically.

SKILL.md

# Convex Security Audit

Comprehensive security review patterns for Convex applications including authorization logic, data access boundaries, action isolation, rate limiting, and protecting sensitive operations.

## Documentation Sources

Before implementing, do not assume; fetch the latest documentation:

- Primary: https://docs.convex.dev/auth/functions-auth
- Production Security: https://docs.convex.dev/production
- For broader context: https://docs.convex.dev/llms.txt

## Instructions

### Security Audit Areas

1. **Authorization Logic** - Who can do what
2. **Data Access Boundaries** - What data users can see
3. **Action Isolation** - Protecting external API calls
4. **Rate Limiting** - Preventing abuse
5. **Sensitive Operations** - Protecting critical functions

### Authorization Logic Audit

#### Role-Based Access Control (RBAC)

```typescript
// convex/lib/auth.ts
import { QueryCtx, MutationCtx } from "./_generated/server";
import { ConvexError } from "convex/values";
import { Doc } from "./_generated/dataModel";

type UserRole = "user" | "moderator" | "admin" | "superadmin";

const roleHierarchy: Record<UserRole, number> = {
  user: 0,
  moderator: 1,
  admin: 2,
  superadmin: 3,
};

export async function getUser(ctx: QueryCtx | MutationCtx): Promise<Doc<"users"> | null> {
  const identity = await ctx.auth.getUserIdentity();
  if (!identity) return null;
  
  return await ctx.db
    .query("users")
    .withIndex("by_tokenIdentifier", (q) => 
      q.eq("tokenIdentifier", identity.tokenIdentifier)
    )
    .unique();
}

export async function requireRole(
  ctx: QueryCtx | MutationCtx, 
  minRole: UserRole
): Promise<Doc<"users">> {
  const user = await getUser(ctx);
  
  if (!user) {
    throw new ConvexError({
      code: "UNAUTHENTICATED",
      message: "Authentication required",
    });
  }
  
  const userRoleLevel = roleHierarchy[user.role as UserRole] ?? 0;
  const requiredLevel = roleHierarchy[minRole];
  
  if (userRoleLevel < requiredLevel) {
    throw new ConvexError({
      code: "FORBIDDEN",
      message: `Role '${minRole}' or higher required`,
    });
  }
  
  return user;
}

// Permission-based check
type Permission = "read:users" | "write:users" | "delete:users" | "admin:system";

const rolePermissions: Record<UserRole, Permission[]> = {
  user: ["read:users"],
  moderator: ["read:users", "write:users"],
  admin: ["read:users", "write:users", "delete:users"],
  superadmin: ["read:users", "write:users", "delete:users", "admin:system"],
};

export async function requirePermission(
  ctx: QueryCtx | MutationCtx,
  permission: Permission
): Promise<Doc<"users">> {
  const user = await getUser(ctx);
  
  if (!user) {
    throw new ConvexError({ code: "UNAUTHENTICATED", message: "Authentication required" });
  }
  
  const userRole = user.role as UserRole;
  const permissions = rolePermissions[userRole] ?? [];
  
  if (!permissions.includes(permission)) {
    throw new ConvexError({
      code: "FORBIDDEN",
      message: `Permission '${permission}' required`,
    });
  }
  
  return user;
}
```

### Data Access Boundaries Audit

```typescript
// convex/data.ts
import { query, mutation } from "./_generated/server";
import { v } from "convex/values";
import { getUser, requireRole } from "./lib/auth";
import { ConvexError } from "convex/values";

// Audit: Users can only see their own data
export const getMyData = query({
  args: {},
  returns: v.array(v.object({
    _id: v.id("userData"),
    content: v.string(),
  })),
  handler: async (ctx) => {
    const user = await getUser(ctx);
    if (!user) return [];
    
    // SECURITY: Filter by userId
    return await ctx.db
      .query("userData")
      .withIndex("by_user", (q) => q.eq("userId", user._id))
      .collect();
  },
});

// Audit: Verify ownership before returning sensitive data
export const getSensitiveItem = query({
  args: { itemId: v.id("sensitiveItems") },
  returns: v.union(v.object({
    _id: v.id("sensitiveItems"),
    secret: v.string(),
  }), v.null()),
  handler: async (ctx, args) => {
    const user = await getUser(ctx);
    if (!user) return null;
    
    const item = await ctx.db.get(args.itemId);
    
    // SECURITY: Verify ownership
    if (!item || item.ownerId !== user._id) {
      return null; // Don't reveal if item exists
    }
    
    return item;
  },
});

// Audit: Shared resources with access list
export const getSharedDocument = query({
  args: { docId: v.id("documents") },
  returns: v.union(v.object({
    _id: v.id("documents"),
    content: v.string(),
    accessLevel: v.string(),
  }), v.null()),
  handler: async (ctx, args) => {
    const user = await getUser(ctx);
    const doc = await ctx.db.get(args.docId);
    
    if (!doc) return null;
    
    // Public documents
    if (doc.visibility === "public") {
      return { ...doc, accessLevel: "public" };
    }
    
    // Must be authenticated for non-public
    if (!user) return null;
    
    // Owner has full access
    if (doc.ownerId === user._id) {
      return { ...doc, accessLevel: "owner" };
    }
    
    // Check shared access
    const access = await ctx.db
      .query("documentAccess")
      .withIndex("by_doc_and_user", (q) => 
        q.eq("documentId", args.docId).eq("userId", user._id)
      )
      .unique();
    
    if (!access) return null;
    
    return { ...doc, accessLevel: access.level };
  },
});
```

### Action Isolation Audit

```typescript
// convex/actions.ts
"use node";

import { action, internalAction } from "./_generated/server";
import { v } from "convex/values";
import { api, internal } from "./_generated/api";
import { ConvexError } from "convex/values";

// SECURITY: Never expose API keys in responses
export const callExternalAPI = action({
  args: { query: v.string() },
  returns: v.object({ result: v.string() }),
  handler: async (ctx, args) => {
    // Verify user is authenticated
    const identity = await ctx.auth.getUserIdentity();
    if (!identity) {
      throw new ConvexE
avoid-feature-creepSkill

Prevent feature creep when building software, apps, and AI-powered products. Use this skill when planning features, reviewing scope, building MVPs, managing backlogs, or when a user says "just one more feature." Helps developers and AI agents stay focused, ship faster, and avoid bloated products.

convex-agentsSkill

Building AI agents with the Convex Agent component including thread management, tool integration, streaming responses, RAG patterns, and workflow orchestration

convex-best-practicesSkill

Guidelines for building production-ready Convex apps covering function organization, query patterns, validation, TypeScript usage, error handling, and the Zen of Convex design philosophy

convex-component-authoringSkill

How to create, structure, and publish self-contained Convex components with proper isolation, exports, and dependency management

convex-cron-jobsSkill

Scheduled function patterns for background tasks including interval scheduling, cron expressions, job monitoring, retry strategies, and best practices for long-running tasks

convex-file-storageSkill

Complete file handling including upload flows, serving files via URL, storing generated files from actions, deletion, and accessing file metadata from system tables

convex-functionsSkill

Writing queries, mutations, actions, and HTTP actions with proper argument validation, error handling, internal functions, and runtime considerations

convex-http-actionsSkill

External API integration and webhook handling including HTTP endpoint routing, request/response handling, authentication, CORS configuration, and webhook signature validation