Skip to main content Startseite Ersteller igorwarzocha opencode-workflows security-convex
security-convex Review Convex security audit patterns for authentication and authorization. Use for auditing query/mutation auth, row-level security, and validators. Use proactively when reviewing Convex apps (convex/ directory present).
Examples:
- user: "Audit these Convex mutations" → check for missing ctx.auth and input validators
- user: "Check for IDOR in Convex queries" → verify ownership checks on document access
- user: "Review Convex HTTP actions" → check for signature verification on webhooks
- user: "Secure these Convex queries" → implement custom functions for enforced auth
- user: "Check for data leaks in subscriptions" → verify filtered result sets
Zur Installation springen Skills Marktplatz Entdecken und erkunden Sie KI-Skills, die von der Community erstellt wurden.
Mit Codex oder Claude installieren Kopieren Sie diesen Prompt, fügen Sie ihn in Codex, Claude oder einen anderen Assistant ein und lassen Sie die Skill-Seite prüfen und installieren.
Prompt kopierenPrompt-Details anzeigen Ein direkter Befehl überspringt den Prüf-Prompt. Prüfen Sie die Quelle, bevor Sie ihn ausführen.
npx skills add https://github.com/IgorWarzocha/Opencode-Workflows --skill security-convexDer Befehl bleibt in einer Zeile. Scrollen Sie horizontal, um ihn vor dem Kopieren vollständig zu prüfen.
Sie bevorzugen eine lokale Kopie? Laden Sie die Dateien herunter, die SkillsMP derzeit vorliegen.
ZIP herunterladen Herunterladen... Mehr aus diesem Repository
Verwandte Berufe SOC
Basierend auf der SOC-Berufsklassifikation
name security-convex description Review Convex security audit patterns for authentication and authorization. Use for auditing query/mutation auth, row-level security, and validators. Use proactively when reviewing Convex apps (convex/ directory present).
Examples:
- user: "Audit these Convex mutations" → check for missing ctx.auth and input validators
- user: "Check for IDOR in Convex queries" → verify ownership checks on document access
- user: "Review Convex HTTP actions" → check for signature verification on webhooks
- user: "Secure these Convex queries" → implement custom functions for enforced auth
- user: "Check for data leaks in subscriptions" → verify filtered result sets
Security audit patterns for Convex applications covering authentication, authorization, input validation, and Convex-specific vulnerabilities.
The #1 Vibecoding Mistake: Unauthenticated Functions
Convex functions are public by default. Every query and mutation is callable from any client unless you add auth checks.
export const listUsers = query ({
handler : async (ctx) => {
return await ctx.db .query ("users" ).collect ();
},
});
export const deleteNote = mutation ({
args : { noteId : v.id ( ) },
: (ctx, args) => {
ctx. . (args. );
},
});
listUsers = ({
: (ctx) => {
userId = (ctx);
(!userId) ( );
ctx. . ( ). ();
},
});
"notes"
handler
async
await
db
delete
noteId
export
const
query
handler
async
const
await
getAuthUserId
if
throw
new
Error
"Unauthenticated"
return
await
db
query
"users"
collect
Authentication Checks
Using Convex Auth import { getAuthUserId } from "@convex-dev/auth/server" ;
export const getMyProfile = query ({
handler : async (ctx) => {
return await ctx.db .query ("users" ).first ();
},
});
export const getMyProfile = query ({
handler : async (ctx) => {
const userId = await getAuthUserId (ctx);
if (!userId) throw new Error ("Unauthenticated" );
return await ctx.db .get (userId);
},
});
Using Third-Party Auth (Clerk, Auth0)
export const sensitiveData = query ({
handler : async (ctx) => {
const identity = await ctx.auth .getUserIdentity ();
return await ctx.db .query ("secrets" ).collect ();
},
});
export const sensitiveData = query ({
handler : async (ctx) => {
const identity = await ctx.auth .getUserIdentity ();
if (!identity) throw new Error ("Unauthenticated" );
return await ctx.db .query ("secrets" )
.filter (q => q.eq (q.field ("userId" ), identity.subject ))
.collect ();
},
});
Authorization: The IDOR Problem Authentication (who you are) ≠ Authorization (what you can access).
export const getNote = query ({
args : { noteId : v.id ("notes" ) },
handler : async (ctx, args) => {
const userId = await getAuthUserId (ctx);
if (!userId) throw new Error ("Unauthenticated" );
return await ctx.db .get (args.noteId );
},
});
export const getNote = query ({
args : { noteId : v.id ("notes" ) },
handler : async (ctx, args) => {
const userId = await getAuthUserId (ctx);
if (!userId) throw new Error ("Unauthenticated" );
const note = await ctx.db .get (args.noteId );
if (!note || note.userId !== userId) {
throw new Error ("Not found" );
}
return note;
},
});
Team/Org Membership Checks
export const getTeamData = query ({
args : { teamId : v.id ("teams" ) },
handler : async (ctx, args) => {
const userId = await getAuthUserId (ctx);
if (!userId) throw new Error ("Unauthenticated" );
return await ctx.db .query ("projects" )
.filter (q => q.eq (q.field ("teamId" ), args.teamId ))
.collect ();
},
});
export const getTeamData = query ({
args : { teamId : v.id ("teams" ) },
handler : async (ctx, args) => {
const userId = await getAuthUserId (ctx);
if (!userId) throw new Error ("Unauthenticated" );
const membership = await ctx.db .query ("teamMembers" )
.withIndex ("by_team_user" , q =>
q.eq ("teamId" , args.teamId ).eq ("userId" , userId)
).first ();
if (!membership) throw new Error ("Not a team member" );
return await ctx.db .query ("projects" )
.filter (q => q.eq (q.field ("teamId" ), args.teamId ))
.collect ();
},
});
Custom Functions Pattern (Recommended) Use convex-helpers to enforce auth by default:
import { customQuery, customMutation } from "convex-helpers/server/customFunctions" ;
import { getAuthUserId } from "@convex-dev/auth/server" ;
export const userQuery = customQuery (query, {
args : {},
input : async (ctx) => {
const userId = await getAuthUserId (ctx);
if (!userId) throw new Error ("Unauthenticated" );
const user = await ctx.db .get (userId);
if (!user) throw new Error ("User not found" );
return { ctx : { user }, args : {} };
},
});
export const getMyNotes = userQuery ({
handler : async (ctx) => {
return await ctx.db .query ("notes" )
.filter (q => q.eq (q.field ("userId" ), ctx.user ._id ))
.collect ();
},
});
Audit: Check if custom functions are used consistently. Search for raw query( and mutation( usage.
Input Validation
Missing Validators
export const createNote = mutation ({
handler : async (ctx, args) => {
await ctx.db .insert ("notes" , args);
},
});
export const createNote = mutation ({
args : {
title : v.string (),
content : v.string (),
isPublic : v.optional (v.boolean ()),
},
handler : async (ctx, args) => {
const userId = await getAuthUserId (ctx);
if (!userId) throw new Error ("Unauthenticated" );
await ctx.db .insert ("notes" , {
...args,
userId,
});
},
});
Trusting Client-Provided IDs for Ownership
export const createNote = mutation ({
args : {
title : v.string (),
userId : v.id ("users" ),
},
handler : async (ctx, args) => {
await ctx.db .insert ("notes" , args);
},
});
export const createNote = mutation ({
args : { title : v.string () },
handler : async (ctx, args) => {
const userId = await getAuthUserId (ctx);
if (!userId) throw new Error ("Unauthenticated" );
await ctx.db .insert ("notes" , {
title : args.title ,
userId,
});
},
});
Internal Functions
export const processPayment = mutation ({
args : { amount : v.number () },
handler : async (ctx, args) => {
await chargeCard (args.amount );
},
});
export const processPayment = internalMutation ({
args : { userId : v.id ("users" ), amount : v.number () },
handler : async (ctx, args) => {
await chargeCard (args.amount );
},
});
export const purchaseItem = mutation ({
args : { itemId : v.id ("items" ) },
handler : async (ctx, args) => {
const userId = await getAuthUserId (ctx);
if (!userId) throw new Error ("Unauthenticated" );
const item = await ctx.db .get (args.itemId );
await ctx.runMutation (internal.payments .processPayment , {
userId,
amount : item.price ,
});
},
});
HTTP Actions
export const webhook = httpAction (async (ctx, request) => {
const body = await request.json ();
await ctx.runMutation (api.data .processWebhook , body);
return new Response ("OK" );
});
export const webhook = httpAction (async (ctx, request) => {
const signature = request.headers .get ("x-webhook-signature" );
const body = await request.text ();
if (!verifySignature (body, signature, process.env .WEBHOOK_SECRET )) {
return new Response ("Unauthorized" , { status : 401 });
}
await ctx.runMutation (api.data .processWebhook , JSON .parse (body));
return new Response ("OK" );
});
Real-Time Subscription Leakage
export const allMessages = query ({
handler : async (ctx) => {
return await ctx.db .query ("messages" ).collect ();
},
});
export const myMessages = query ({
handler : async (ctx) => {
const userId = await getAuthUserId (ctx);
if (!userId) return [];
const myChannels = await ctx.db .query ("channelMembers" )
.filter (q => q.eq (q.field ("userId" ), userId))
.collect ();
const channelIds = myChannels.map (m => m.channelId );
return await ctx.db .query ("messages" )
.filter (q => q.or (...channelIds.map (id =>
q.eq (q.field ("channelId" ), id)
)))
.collect ();
},
});
Environment Variables
const apiKey = process.env .STRIPE_SECRET_KEY ;
export const callStripe = action ({
handler : async (ctx) => {
const apiKey = process.env .STRIPE_SECRET_KEY ;
if (!apiKey) throw new Error ("STRIPE_SECRET_KEY not configured" );
},
});
Note: Environment variables are only available in actions, not queries/mutations.
Common Vulnerabilities Summary Issue Where to Look Severity No auth check in query/mutation All query({ and mutation({ CRITICAL IDOR (no ownership check) Functions with document IDs in args HIGH Client-provided userId Args with v.id("users") HIGH Missing validators Functions without args: {} HIGH Public function for internal logic Sensitive business logic HIGH HTTP action without auth httpAction(HIGH Subscription data leak Queries returning collections MEDIUM Raw query/mutation (no custom fn) Not using userQuery/userMutation MEDIUM
Quick Audit Commands
rg "export const .* = query\(" convex/
rg "export const .* = mutation\(" convex/
rg -l "export const .* = (query|mutation)\(" convex/ | \
xargs rg -L "(getAuthUserId|getUserIdentity)"
rg 'userId: v\.id\("users"\)' convex/
rg "handler: async \(ctx\)" convex/ -g "*.ts"
rg "httpAction" convex/
rg "internalMutation|internalQuery|internalAction" convex/
Hardening Checklist