avoid-feature-creep
Prevent feature creep when building software, apps, and AI-powered products. Use this skill when planning features, reviewing scope, building MVPs, managing…
Deep security review patterns for authorization logic, data access boundaries, action isolation, rate limiting, and protecting sensitive operations
$ npx -y skills add waynesutton/convexskills --skill convex-security-audit --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
/convex-security-auditContext preview
The summary Claude sees to decide when to auto-load this skill.
Deep security review patterns for authorization logic, data access boundaries, action isolation, rate limiting, and protecting sensitive operations
name: convex-security-audit displayName: Convex Security Audit description: Deep security review patterns for authorization logic, data access boundaries, action isolation, rate limiting, and protecting sensitive operations version: 1.0.0 author: Convex tags: [convex, security, audit, authorization, rate-limiting, protection]
Comprehensive security review patterns for Convex applications including authorization logic, data access boundaries, action isolation, rate limiting, and protecting sensitive operations.
Before implementing, do not assume; fetch the latest documentation:
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
// 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;
}// 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 };
},
});// 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"Official Convex plugins for AI coding agents, providing development tools for building reactive backends with TypeScript.
Repo: waynesutton/convexskills
Prevent feature creep when building software, apps, and AI-powered products. Use this skill when planning features, reviewing scope, building MVPs, managing…
Building AI agents with the Convex Agent component including thread management, tool integration, streaming responses, RAG patterns, and workflow orchestration
Guidelines for building production-ready Convex apps covering function organization, query patterns, validation, TypeScript usage, error handling, and the Zen…
How to create, structure, and publish self-contained Convex components with proper isolation, exports, and dependency management
Scheduled function patterns for background tasks including interval scheduling, cron expressions, job monitoring, retry strategies, and best practices for…
Complete file handling including upload flows, serving files via URL, storing generated files from actions, deletion, and accessing file metadata from system…