- update(): WebhookUrlGuard блокирует сохранение private/reserved/loopback IP →
422 validation error на target_url; небезопасные адреса не попадают в БД,
любой будущий потребитель (test() + outbound-доставка) читает только безопасные
- NB: будущая outbound-доставка обязана ВДОБАВОК звать guard перед отправкой
(DNS-rebinding); outbound-pipeline пока не построен (комментарий в update())
- тесты: +PUT private-IP→422 не сохраняет; webhook target_url → публичные
IP-литералы (убрал DNS-резолюцию example.ru-хостов, webhook-suite 93s→5s)
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Code-quality review of Task 4: adds a cross-tenant isolation test
(verifies the where(tenant_id) guard, matching ApiKeyControllerTest)
and a test()-endpoint failure-path test (HTTP 500 -> ok=false). Drops
the @return docblock from OutboundWebhookSubscriptionFactory for
consistency with ApiKeyFactory, eliminating a baseline entry at source.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Audit J5/D4/D5: the outbound_webhook_subscriptions table existed in
schema but had zero code. Adds the OutboundWebhookSubscription model +
factory and WebhookSettingsController with GET/PUT
/api/tenants/me/webhook-settings (one subscription per tenant; secret
generated + returned once on creation, bcrypt-hashed) and POST
/api/webhooks/test (unsigned connectivity check — HMAC-signed event
delivery is a separate post-MVP epic). Tenant-scoped via auth:sanctum +
tenant middleware.
phpstan-baseline.neon: additive-only entries for new test file
(Pest\PendingCalls\TestCall false-positives — documented project pattern)
and OutboundWebhookSubscriptionFactory method.childReturnType (same
pattern as ProjectFactory/TenantFactory/UserFactory already in baseline).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>