fix(payments): verify member_id belongs to caller's space (IDOR)
logCashPayment and linkPaymentToMember accepted a uuid member_id without checking it belongs to the caller's space (the equipment/class paths were already hardened for exactly this). Look up the member scoped to the caller's space and reject a foreign id before writing. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
1aa0cd818a
commit
3243e4ce98
@@ -36,6 +36,19 @@ export async function logCashPayment(formData: {
|
|||||||
|
|
||||||
const transactionDate = v.data.transaction_date ?? new Date().toISOString()
|
const transactionDate = v.data.transaction_date ?? new Date().toISOString()
|
||||||
|
|
||||||
|
// A supplied member_id must belong to the caller's space before we stamp it
|
||||||
|
// onto the payment, mirroring reserveEquipment. Without this a treasurer
|
||||||
|
// could link a cash payment to a member id from another space.
|
||||||
|
if (v.data.member_id) {
|
||||||
|
const { data: tgt } = await supabase
|
||||||
|
.from('space_members')
|
||||||
|
.select('id')
|
||||||
|
.eq('id', v.data.member_id)
|
||||||
|
.eq('space_id', member.space_id)
|
||||||
|
.maybeSingle()
|
||||||
|
if (!tgt) return { error: 'That member was not found in this space.' }
|
||||||
|
}
|
||||||
|
|
||||||
const { data, error } = await supabase
|
const { data, error } = await supabase
|
||||||
.from('payments')
|
.from('payments')
|
||||||
.insert({
|
.insert({
|
||||||
@@ -92,6 +105,17 @@ export async function linkPaymentToMember(paymentId: string, memberId: string) {
|
|||||||
if (!auth.ok) return { error: auth.error }
|
if (!auth.ok) return { error: auth.error }
|
||||||
const { member: self } = auth
|
const { member: self } = auth
|
||||||
|
|
||||||
|
// The target member must belong to the caller's space before we link the
|
||||||
|
// payment to it, mirroring reserveEquipment. Without this a treasurer could
|
||||||
|
// link a payment to a member id from another space.
|
||||||
|
const { data: tgt } = await supabase
|
||||||
|
.from('space_members')
|
||||||
|
.select('id')
|
||||||
|
.eq('id', v.data.memberId)
|
||||||
|
.eq('space_id', self.space_id)
|
||||||
|
.maybeSingle()
|
||||||
|
if (!tgt) return { error: 'That member was not found in this space.' }
|
||||||
|
|
||||||
const { data: payment } = await supabase
|
const { data: payment } = await supabase
|
||||||
.from('payments')
|
.from('payments')
|
||||||
.select('amount, transaction_date')
|
.select('amount, transaction_date')
|
||||||
|
|||||||
Reference in New Issue
Block a user