Operations

Pre-release security checklist

Use this checklist before production.

Must pass

  • [ ] All routes/collections.php actions use Spatie permission: middleware (bulk via FormRequest)
  • [ ] Form requests for collections authorize with the same permissions
  • [ ] Pest: verified user without collection perms → 403 on schema/item writes (CollectionAuthorizationTest)
  • [ ] Pest: user with perms → OK; super-admin bypass still works
  • [ ] collection_permissions.rules.fields strip/deny unreadable/unwritable fields (REST + admin)
  • [ ] item_filter hides/blocks items that do not match
  • [ ] Roles UI can edit field ACL + item filter (Fields & item filter panel)
  • [ ] GraphQL /api/graphql uses ResolveApiAccess + same guards + throttle:api
  • [ ] Project settings / appearance gated by can-manage-project-settings
  • [ ] If two_factor_required is on in production, users without Fortify TOTP and without a passkey are forced to /settings/security with a clear required-MFA banner (TOTP or passkey satisfies; optional personal 2FA/passkeys still work when the setting is off) — decision table
  • [ ] Passkeys: php artisan migrate applied (passkeys table); APP_URL origin matches WebAuthn relying_party_id / allowed_origins; HTTPS in production (passkeys need a secure context; rest of CMS can be HTTP but prefer HTTPS); Fortify confirmPassword still on for security settings; no vendor forks/patches of Fortify or @laravel/passkeys; RP checks not widened — Passkeys
  • [ ] Smoke: register passkey on /settings/security, sign in with Sign in with passkey, Confirm with passkey on /user/confirm-password (HTTPS / secure context), and password confirm + TOTP paths still work (password confirm also on HTTP when the passkey CTA is hidden) — Confirm password with a passkey
  • [ ] No collections admin endpoint without Gate/permission middleware

Should verify

  • [ ] Migrations applied: passkeys + collection_item_revisions + M2M junction object migration + user_created_id / user_updated_id on collections_items
  • [ ] Revisions restore creates a new revision (History UI)
  • [ ] M2M junction object shape documented for API consumers; item form preserves meta on save
  • [ ] Jobs monitor gated by can-show-jobs / can-manage-jobs
  • [ ] GraphQL query cache mode is opcache (or Redis), not database-serialized AST objects
  • [ ] Rate limit / CORS for /api/v1 and /api/graphql reviewed for the target environment
  • [ ] Production: set project public_api_allowed_origins (or explicit CORS_ALLOWED_ORIGINS); do not rely on Origin alone — use API key + IP for strong protection (Origin allowlist)
  • [ ] item_filter SQL pushdown: list meta.total / pages match filtered set (not post-paginate PHP)
  • [ ] Items Export CSV/JSON respects filters + cap (CollectionItemExportService)
  • [ ] TipTap wysiwyg: toolbar works; script paste stripped by WysiwygHtmlSanitizer
  • [ ] Outbound webhook URL + secret configured only when needed; secret not exposed in shared Inertia props; queue worker delivers DeliverOutboundWebhookJob

Pest smoke (PHP 8.4)

herd php artisan test --filter='CollectionAuthorization|CollectionPermissionRules|Graphql|AdvancedFilters|CollectionItemRevision|CollectionItemAudit|CollectionItemExport|WysiwygSanitize|JobMonitor|many to many accepts junction'
Previous
Deployment