mirror of
https://github.com/Luchexx/wacrm.git
synced 2026-11-02 09:19:51 +00:00
Merge pull request #351 from ArnasDon/fix/ghsa-34q7-fv77-625j-role-checks
fix(security): enforce agent role on service-role write routes (GHSA-34q7-fv77-625j)
This commit is contained in:
commit
03e851bea5
@ -1,5 +1,6 @@
|
||||
import { NextResponse } from 'next/server'
|
||||
import { createClient } from '@/lib/supabase/server'
|
||||
import { requireRole, toErrorResponse } from '@/lib/auth/account'
|
||||
import { supabaseAdmin } from '@/lib/automations/admin-client'
|
||||
|
||||
export async function POST(
|
||||
@ -7,6 +8,16 @@ export async function POST(
|
||||
{ params }: { params: Promise<{ id: string }> },
|
||||
) {
|
||||
const { id } = await params
|
||||
|
||||
// Duplicating creates a new automation row — a write. Enforce `agent`
|
||||
// (the service-role client below bypasses the agent-gated
|
||||
// automations_insert RLS).
|
||||
try {
|
||||
await requireRole('agent')
|
||||
} catch (err) {
|
||||
return toErrorResponse(err)
|
||||
}
|
||||
|
||||
const supabase = await createClient()
|
||||
const {
|
||||
data: { user },
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import { NextResponse } from 'next/server'
|
||||
import { createClient } from '@/lib/supabase/server'
|
||||
import { requireRole, toErrorResponse } from '@/lib/auth/account'
|
||||
import { supabaseAdmin } from '@/lib/automations/admin-client'
|
||||
import {
|
||||
loadStepsTree,
|
||||
@ -47,6 +48,16 @@ export async function PATCH(
|
||||
{ params }: { params: Promise<{ id: string }> },
|
||||
) {
|
||||
const { id } = await params
|
||||
|
||||
// Editing an automation is a write — the RLS automations_update policy
|
||||
// requires `agent`, but this route mutates via the service-role client
|
||||
// which bypasses RLS, so enforce the role here.
|
||||
try {
|
||||
await requireRole('agent')
|
||||
} catch (err) {
|
||||
return toErrorResponse(err)
|
||||
}
|
||||
|
||||
const user = await requireUser()
|
||||
if (!user) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
|
||||
|
||||
@ -125,6 +136,15 @@ export async function DELETE(
|
||||
{ params }: { params: Promise<{ id: string }> },
|
||||
) {
|
||||
const { id } = await params
|
||||
|
||||
// Deleting an automation is a write — enforce `agent` (the service-role
|
||||
// client below bypasses the agent-gated automations_delete RLS).
|
||||
try {
|
||||
await requireRole('agent')
|
||||
} catch (err) {
|
||||
return toErrorResponse(err)
|
||||
}
|
||||
|
||||
const user = await requireUser()
|
||||
if (!user) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { NextResponse } from 'next/server'
|
||||
import { getCurrentAccount, toErrorResponse } from '@/lib/auth/account'
|
||||
import { requireRole, toErrorResponse } from '@/lib/auth/account'
|
||||
import { runAutomationsForTrigger } from '@/lib/automations/engine'
|
||||
import type { AutomationTriggerType } from '@/types'
|
||||
|
||||
@ -9,9 +9,11 @@ import type { AutomationTriggerType } from '@/types'
|
||||
* account_id and dispatch over the account's automations.
|
||||
*/
|
||||
export async function POST(request: Request) {
|
||||
// Firing automations sends outbound WhatsApp — a write action. Require
|
||||
// at least `agent`; a viewer must not be able to trigger sends.
|
||||
let accountId: string
|
||||
try {
|
||||
const ctx = await getCurrentAccount()
|
||||
const ctx = await requireRole('agent')
|
||||
accountId = ctx.accountId
|
||||
} catch (err) {
|
||||
return toErrorResponse(err)
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import { NextResponse } from 'next/server'
|
||||
import { createClient } from '@/lib/supabase/server'
|
||||
import { requireRole, toErrorResponse } from '@/lib/auth/account'
|
||||
import { supabaseAdmin } from '@/lib/automations/admin-client'
|
||||
import { getTemplate } from '@/lib/automations/templates'
|
||||
import { insertSteps, type BuilderStepInput } from '@/lib/automations/steps-tree'
|
||||
@ -24,6 +25,15 @@ export async function GET() {
|
||||
}
|
||||
|
||||
export async function POST(request: Request) {
|
||||
// Creating an automation is a write — the RLS automations_insert policy
|
||||
// requires `agent`, but this route inserts via the service-role client
|
||||
// which bypasses RLS, so the role must be enforced here.
|
||||
try {
|
||||
await requireRole('agent')
|
||||
} catch (err) {
|
||||
return toErrorResponse(err)
|
||||
}
|
||||
|
||||
const supabase = await createClient()
|
||||
const {
|
||||
data: { user },
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import { NextResponse } from 'next/server'
|
||||
import { createClient } from '@/lib/supabase/server'
|
||||
import { requireRole, toErrorResponse } from '@/lib/auth/account'
|
||||
import { supabaseAdmin } from '@/lib/flows/admin-client'
|
||||
import { validateFlowForActivation } from '@/lib/flows/validate'
|
||||
|
||||
@ -23,6 +24,16 @@ export async function POST(
|
||||
) {
|
||||
const { id } = await context.params
|
||||
|
||||
// Changing status (activate / draft / archive) is a write — the RLS
|
||||
// flows_update policy requires `agent`, but the service-role client
|
||||
// below bypasses RLS, so enforce the role here (a viewer passes the
|
||||
// membership-only ownership check).
|
||||
try {
|
||||
await requireRole('agent')
|
||||
} catch (err) {
|
||||
return toErrorResponse(err)
|
||||
}
|
||||
|
||||
const supabase = await createClient()
|
||||
const {
|
||||
data: { user },
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import { NextResponse } from 'next/server'
|
||||
import { createClient } from '@/lib/supabase/server'
|
||||
import { requireRole, toErrorResponse } from '@/lib/auth/account'
|
||||
import { supabaseAdmin } from '@/lib/flows/admin-client'
|
||||
|
||||
/**
|
||||
@ -91,6 +92,16 @@ export async function PUT(
|
||||
context: { params: Promise<{ id: string }> },
|
||||
) {
|
||||
const { id } = await context.params
|
||||
|
||||
// Writes require at least `agent` — the RLS flows_update policy demands
|
||||
// it, but this route mutates via the service-role client which bypasses
|
||||
// RLS, so the role must be enforced here (a viewer passes ownership).
|
||||
try {
|
||||
await requireRole('agent')
|
||||
} catch (err) {
|
||||
return toErrorResponse(err)
|
||||
}
|
||||
|
||||
const guard = await requireOwnership(id)
|
||||
if (!guard.ok) return NextResponse.json(guard.body, { status: guard.status })
|
||||
|
||||
@ -177,6 +188,15 @@ export async function DELETE(
|
||||
context: { params: Promise<{ id: string }> },
|
||||
) {
|
||||
const { id } = await context.params
|
||||
|
||||
// Writes require at least `agent` — see the PUT handler note. The
|
||||
// service-role client below bypasses the agent-gated flows_delete RLS.
|
||||
try {
|
||||
await requireRole('agent')
|
||||
} catch (err) {
|
||||
return toErrorResponse(err)
|
||||
}
|
||||
|
||||
const guard = await requireOwnership(id)
|
||||
if (!guard.ok) return NextResponse.json(guard.body, { status: guard.status })
|
||||
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import { NextResponse } from 'next/server'
|
||||
import { createClient } from '@/lib/supabase/server'
|
||||
import { requireRole, toErrorResponse } from '@/lib/auth/account'
|
||||
import { supabaseAdmin } from '@/lib/flows/admin-client'
|
||||
import { getFlowTemplate } from '@/lib/flows/templates'
|
||||
|
||||
@ -45,6 +46,15 @@ export async function GET() {
|
||||
}
|
||||
|
||||
export async function POST(request: Request) {
|
||||
// Creating a flow is a write — the RLS flows_insert policy requires
|
||||
// `agent`, but this route inserts via the service-role client which
|
||||
// bypasses RLS, so the role must be enforced here.
|
||||
try {
|
||||
await requireRole('agent')
|
||||
} catch (err) {
|
||||
return toErrorResponse(err)
|
||||
}
|
||||
|
||||
const guard = await requireUser()
|
||||
if (!guard.ok) {
|
||||
return NextResponse.json(guard.body, { status: guard.status })
|
||||
|
||||
Loading…
Reference in New Issue
Block a user