diff --git a/app/app/Http/Controllers/Api/SupplierWebhookController.php b/app/app/Http/Controllers/Api/SupplierWebhookController.php index 69f8ada8..93f44069 100644 --- a/app/app/Http/Controllers/Api/SupplierWebhookController.php +++ b/app/app/Http/Controllers/Api/SupplierWebhookController.php @@ -30,6 +30,12 @@ use Symfony\Component\HttpFoundation\IpUtils; * * Backward-compat: legacy /api/webhook/{token} (per-tenant) живёт параллельно * на WebhookReceiveController — не пересекается. + * + * ⚠️ PRODUCTION: пустой `supplier_ip_allowlist = '[]'` (или невалидный JSON, + * который `?: []` молча проглатывает) делает endpoint достижимым с любого IP — + * только secret защищает. На prod admin ОБЯЗАН заполнить allowlist реальными + * IP/CIDR поставщика. Дополнительно: hash_equals на уровне `verifySecret` + * timing-safe только при равной длине; rate-limit per-IP добавится в Plan 3+. */ class SupplierWebhookController extends Controller { diff --git a/app/app/Jobs/RouteSupplierLeadJob.php b/app/app/Jobs/RouteSupplierLeadJob.php index 6cdb3298..ac3114a1 100644 --- a/app/app/Jobs/RouteSupplierLeadJob.php +++ b/app/app/Jobs/RouteSupplierLeadJob.php @@ -254,6 +254,12 @@ class RouteSupplierLeadJob implements ShouldQueue * Сохраняет упавший job в `failed_webhook_jobs` (tenant_id=null — sharing-flow, * tenant ещё не определён на момент routing'а) для ручного разбора. * supplier_lead.error апдейтится текстом исключения. + * + * ⚠️ PRODUCTION: failed_webhook_jobs имеет RLS-policy `tenant_isolation USING + * (tenant_id = current_setting('app.current_tenant_id')::bigint)`. INSERT с + * tenant_id=NULL под non-BYPASSRLS ролью молча отклонится (NULL = ::bigint → NULL). + * Queue-worker обязан подключаться к БД под elevated-ролью (postgres / service-account + * с BYPASSRLS) ИЛИ нужен INSERT-policy WITH CHECK (true) на эту таблицу — Plan 3+. */ public function failed(Throwable $e): void {