security: fix 6 critical RLS vulns, SHA-256 hash, DSGVO [GELÖSCHT], completed status
Some checks failed
Staging Build / build (push) Has been cancelled

This commit is contained in:
DanielS
2026-07-06 22:39:05 +02:00
parent 3d938d29fc
commit 2d44dd4c58
7 changed files with 139 additions and 22 deletions

View File

@@ -167,17 +167,17 @@ export async function anonymizeEndCustomer(id: string): Promise<void> {
.from('end_customers')
.update({
company_name: '[GELÖSCHT]',
vat_id: null,
vat_id: '[GELÖSCHT]',
first_name: '[GELÖSCHT]',
last_name: '[GELÖSCHT]',
street: '[GELÖSCHT]',
zip: null,
city: null,
email: null,
bank_iban: null,
bank_bic: null,
bank_name: null,
bank_owner: null,
zip: '[GELÖSCHT]',
city: '[GELÖSCHT]',
email: '[GELÖSCHT]',
bank_iban: '[GELÖSCHT]',
bank_bic: '[GELÖSCHT]',
bank_name: '[GELÖSCHT]',
bank_owner: '[GELÖSCHT]',
is_anonymized: true,
})
.eq('id', id)

View File

@@ -1,6 +1,7 @@
'use server'
import { createClient } from '@/lib/supabase/server'
import { createHash } from 'crypto'
import { createAdminClient } from '@/lib/supabase/admin'
import { revalidatePath } from 'next/cache'
import { sendMail } from '@/utils/mail'
@@ -25,18 +26,12 @@ function generateOrderNumber(): string {
}
/**
* Erstellt einen einfachen deterministischen Hash aus dem Bestell-Snapshot.
* Erstellt einen SHA-256 Hash aus dem Bestell-Snapshot.
* Wird für den Idempotenz-Guard verwendet (verhindert Doppelbestellungen).
*/
function hashOrderSnapshot(snapshot: object): string {
const str = JSON.stringify(snapshot)
let hash = 0
for (let i = 0; i < str.length; i++) {
const char = str.charCodeAt(i)
hash = ((hash << 5) - hash) + char
hash = hash & hash // 32-bit integer
}
return Math.abs(hash).toString(36)
return createHash('sha256').update(str).digest('hex')
}
// ─── submitOrder ──────────────────────────────────────────────────────────────
@@ -155,16 +150,16 @@ export async function submitOrder(params: {
const customerSnapshot = buildCustomerSnapshot(customerProfile, endCustomer ?? null)
const orderSnapshot = buildOrderSnapshot(selections, dbProducts, dbCategories, billingInterval, moduleQuantities, lastLicenseDate)
// 2. Idempotenz-Guard: Prüfen ob identische Bestellung in den letzten 30s existiert
// 2. Idempotenz-Guard: Prüfen ob identische Bestellung in den letzten 5 Minuten existiert
const orderHash = hashOrderSnapshot(orderSnapshot)
const thirtySecondsAgo = new Date(Date.now() - 30_000).toISOString()
const fiveMinutesAgo = new Date(Date.now() - 5 * 60_000).toISOString()
const { data: existingOrder } = await supabase
.from('orders')
.select('*')
.eq('user_id', user.id)
.eq('order_hash', orderHash)
.gte('created_at', thirtySecondsAgo)
.gte('created_at', fiveMinutesAgo)
.maybeSingle()
if (existingOrder) {

View File

@@ -1,11 +1,15 @@
import { createClient } from '@supabase/supabase-js'
export function createAdminClient() {
const serviceRoleKey = process.env.SUPABASE_SERVICE_ROLE_KEY || 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6InNlcnZpY2Vfcm9sZSIsImV4cCI6MTk4MzgxMjk5Nn0.EGIM96RAZx35lJzdJsyH-qQwv8Hdp7fsn3W0YpN81IU'
const supabaseUrl = process.env.SUPABASE_URL || process.env.NEXT_PUBLIC_SUPABASE_URL;
const serviceRoleKey = process.env.SUPABASE_SERVICE_ROLE_KEY;
if (!supabaseUrl || !serviceRoleKey) {
throw new Error('SUPABASE_URL and SUPABASE_SERVICE_ROLE_KEY must be set in environment variables.');
}
return createClient(
supabaseUrl!,
supabaseUrl,
serviceRoleKey,
{
auth: {
@@ -15,3 +19,4 @@ export function createAdminClient() {
}
)
}