# Security

Security baseline:

- Passwords use `password_hash(..., PASSWORD_BCRYPT)`.
- Forms require CSRF protection through Yii middleware/configuration.
- Use ORM/query builder/prepared statements for database access.
- Enforce RBAC permissions through `PermissionService`.
- Protect login, checkout, scanner, and API endpoints with rate limiting.
- Stripe webhooks require signature verification.
- QR tokens are signed random references and must not contain personal data.
- Uploads must validate MIME type, extension, size, and storage path.
- Production error pages must not expose stack traces.
- Cookies must be secure, HTTP-only, and SameSite-aware in production.
- Audit important admin and organiser actions.

OWASP checks should focus first on auth, access control, injection, secrets, file uploads, webhook spoofing, and QR replay/guessing.

## Media Upload Hardening

Local MVP uploads now enforce JPEG, PNG, and WebP only; 5MB maximum size; readable image dimensions; risky executable filename blocking; script-like content checks; SHA-256 metadata; scan status metadata; optional ClamAV scanning through `CLAMAV_CLAMSCAN_PATH`; and WebP display/thumb variants when the GD extension is available. In `APP_ENV=prod` or `APP_ENV=production`, or when `MEDIA_MALWARE_SCAN_REQUIRED=true`, uploads fail closed unless `CLAMAV_CLAMSCAN_PATH` is configured.

Uploaded files are stored under `public/uploads`, with an `.htaccess` guard to block script execution. Existing assets can be rescanned through `php bin/rescan-media-assets --force --limit=100` or the platform admin System Health media panel; scan outcomes are persisted in `media_assets` and each scan run is logged in `media_scan_runs`. System health feeds durable admin alerts and the alert-notification outbox, so failed or infected scans can be queued to operational recipients once the platform policy is enabled. Production should still add managed malware scanning infrastructure, CDN/private storage rules, retention policy, and deeper floor-plan processing.

Customer-facing event, product, and site-branding media must only use active, non-deleted uploaded assets with `scan_status = clean`. Admin and organiser media libraries may still show blocked or failed assets so staff can review, replace, or remove them.

Media variants are rendered server-side from the original clean upload and respect crop/focal/rotation metadata. This prevents customer-facing pages from relying only on browser-side preview state for image positioning.

Production readiness checks warn if the PHP GD extension is unavailable and mark missing malware scanning as critical for production media uploads. Enable GD in XAMPP/staging/production PHP before relying on image crop rendering or generated WebP variants, and configure ClamAV before allowing public organiser media uploads in production.

Media retention is non-destructive by default: the admin and CLI paths first produce a dry-run report for orphaned uploads, unsafe scan states, and old archived assets. Supplying an explicit soft-archive action hides eligible active media from public use but does not delete files from disk; permanent deletion remains a manual production storage policy decision.

Media storage readiness is explicit. `MEDIA_STORAGE_DRIVER=local_public` is accepted for local XAMPP testing but warns in production. Set `MEDIA_STORAGE_DRIVER=cdn` or `object_storage` with an HTTPS `MEDIA_PUBLIC_BASE_URL` before high-volume public organiser media goes live.

## Deployment Monitoring

The platform admin System Health view reports runtime, database, upload storage, media scan status, Stripe readiness, webhook queue, delivery queue, stale checkout drafts, deployment secrets, debug mode, upload execution guard, and security header readiness.
