Skip to content

Media pipeline fixes + Docker security hardening#118

Merged
fabiodalez-dev merged 5 commits into
mainfrom
fix/media-docker-security-hardening
Jul 8, 2026
Merged

Media pipeline fixes + Docker security hardening#118
fabiodalez-dev merged 5 commits into
mainfrom
fix/media-docker-security-hardening

Conversation

@fabiodalez-dev

Copy link
Copy Markdown
Owner

What this PR does

Deep audit of the upload → thumbnail → serving pipeline, plus security hardening of the Docker image after a scanner reported 307 CVEs against fabiodalez/cimaise:latest.

Media pipeline fixes (fix(media))

  • Originals were never deleted from disk: the delete paths resolved <root>/app/storage/... (wrong dirname depth) so the @unlink silently no-oped. I also discovered originals are shared between images (sha1 dedup on upload + attach copying original_path, up to 23 rows per file in my data), so a naive path fix would have caused data loss. Deletions now go through UploadService::deleteOriginalIfUnreferenced(), which unlinks only when no surviving images row references the file.
  • AVIF variants never generated without Imagick: web SAPIs with GD only (no Imagick/vips) failed every AVIF encode and 404'd forever. Added a vips attempt + GD imageavif() fallback, mirroring the WebP fallback. Verified live: a missing md.avif now generates on demand under GD.
  • Denied media responses were cached as public, immutable: the blur/placeholder served under the sharp variant URL stuck in the browser for up to 7 days after unlocking a protected album. Denial responses are now private, no-store.
  • Daily variant cron churn: the maintenance service counted jxl (CLI-only) as an expected format, so every image looked incomplete and the whole library was re-scanned daily. It now counts only formats the runtime can generate, and reconciles protected-album variants back out of public/media on every pass.
  • X-CSRF-Token removed from image responses — a shared cache could store one visitor's session-bound token and replay it to others.

Dependency security (build(deps))

  • TinyMCE 6.8.6 → 8.7.0 (5 XSS advisories; license_key: 'gpl' added to the init as required from v7).
  • Swiper 11 → 14.0.2 (critical prototype pollution). Bundle API unchanged at the call sites; checked home + album pages in a browser, no JS errors.
  • lodash / preact patched. npm audit --omit=dev and composer audit are both clean; the remaining npm advisories are dev-only tooling (vite/esbuild dev server) that never ships.

Docker hardening (feat(docker))

  • Base php:8.3-apache-bookwormphp:8.5-apache-trixie + a dist-upgrade layer: the flagged system packages are now libheif 1.19.8, libde265 1.0.15, openexr 3.1.13, perl 5.40. Node stage 18 → 22. Imagick builds fine on PHP 8.5 (AVIF + HEIC verified in the container).
  • Fixed missing translation keys in containers: the base language packs were excluded by .dockerignore and shadowed by the storage volume. They are now baked into the image and re-seeded by the entrypoint on every boot, which also heals existing volumes on upgrade.
  • Global ServerName kills the AH00558 startup warning.
  • Zero-typing MySQL install: compose passes the bundled DB coordinates as CIMAISE_DB_* env vars and the installer preselects MySQL with every field prefilled. Defaults are overridable via CIMAISE_MYSQL_* in a .env next to the compose file; the MySQL service publishes no host port. A first-boot banner documents the port mapping and the effective credentials until the installer runs.

Testing

  • PHPStan: 0 errors project-wide (includes a new scan-only stub for the optional vips classes).
  • PHPUnit: 142 tests green (fixed the 2 MigrationsTest errors introduced with the 1.4.18 migration).
  • Live-verified on the dev server (headers, 304s, on-demand AVIF, denial no-store) and inside the built image (healthy, translations seeded, prefilled installer, no AH00558).

Note: the published image only picks these fixes up with the next release tag (v* → docker-publish workflow).

…ials

- Image/album deletion resolved originals against <root>/app (dirname depth
  off by one) so the @Unlink never fired and originals piled up on disk.
  Deletions now go through UploadService::deleteOriginalIfUnreferenced(),
  which also refuses to unlink an original still referenced by another
  images row — originals are shared by design (sha1 dedup + attach).
- generateVariantsForImage() could only produce AVIF through Imagick; on
  web SAPIs without Imagick/vips every AVIF request failed and returned
  404 forever. Added a vips attempt and a GD imageavif() fallback
  (mirroring the existing WebP fallback).
- Access-denied media responses (blur/placeholder served under the sharp
  variant URL) were cacheable `public, immutable`, so browsers kept the
  blur long after the visitor unlocked the album. They are now
  `private, no-store`.
- VariantMaintenanceService now counts only formats the runtime can
  actually generate (jxl is CLI-only) — previously every image looked
  perpetually incomplete and the daily cron re-scanned the whole library
  on each run. It also reconciles protected-album variants back out of
  public/media on every daily pass.
- CsrfMiddleware no longer adds X-CSRF-Token to image responses: a shared
  cache could store one visitor's session-bound token and replay it.
- MigrationsTest seeded no plugin_status table, so the 1.4.18 migration
  (which deactivates the retired bundled plugins there) crashed both
  apply-in-order and idempotency tests. Seed the pre-1.4.18 shape, assert
  the retired plugins end up deactivated, and fix the marker-settings
  assertion (1.4.18 deletes two of the markers 1.2.0 inserted).
- PHPStan could not resolve the optional Jcupitt\Vips\Image class used
  behind class_exists() guards in ImageEngine (4 class.notFound errors).
  Added a scan-only stub instead of composer-requiring jcupitt/vips,
  which would drag in ext-ffi.
Fixes every production npm advisory reported against the app bundle:
- tinymce 6.8.6 -> 8.7.0 (five XSS advisories); the init now declares
  license_key 'gpl', required for self-hosted TinyMCE 7+.
- swiper 11 -> 14.0.2 (prototype-pollution, critical). The galleries use
  the stable bundle API (new Swiper + navigation/pagination/thumbs), no
  call-site changes needed.
- lodash / preact patched via npm audit.
Assets rebuilt (vite + copy-vendor + FA subset); non-hashed app.css and
admin-app.css synced from the manifest. `npm audit --omit=dev` is clean;
remaining advisories affect dev-only tooling that never ships.
…aller

- Base image php:8.3-apache-bookworm -> php:8.5-apache-trixie plus a
  dist-upgrade layer: clears the CVE backlog scanners reported against
  the old imaging libraries (libheif, libde265, openexr, perl). Assets
  stage moves to Node 22. Imagick builds and serves AVIF/HEIC on 8.5.
- The base language packs (storage/translations/*.json) were excluded by
  .dockerignore AND shadowed by the storage volume, so containers ran
  with raw translation keys. They are now baked into
  /usr/share/cimaise/translations and re-seeded by the entrypoint on
  every boot, which also heals existing volumes on upgrade.
- Global ServerName silences Apache's AH00558 startup warning.
- The compose file passes the bundled MySQL coordinates to the app as
  CIMAISE_DB_* variables; installer.php reads them, preselects MySQL and
  prefills the whole form — no credentials to look up or type. Defaults
  are overridable via CIMAISE_MYSQL_* in a .env next to the compose file,
  and the MySQL service still publishes no host port.
- First-boot entrypoint banner documents the port mapping (-p 8080:80)
  and the effective database coordinates until the installer has run.
@coderabbitai

coderabbitai Bot commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Too many files!

This PR contains 238 files, which is 88 over the limit of 150.

To get a review, narrow the scope:
• coderabbit review --type committed # exclude uncommitted changes
• coderabbit review --dir # limit to a subdirectory
• coderabbit review --base # compare against a closer base

Upgrade to a paid plan to raise the limit.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 451c17c7-0c39-42ef-90fa-9f3780a1d8e7

📥 Commits

Reviewing files that changed from the base of the PR and between f2b84a9 and 5bb1cab.

⛔ Files ignored due to path filters (38)
  • package-lock.json is excluded by !**/package-lock.json
  • public/assets/vendor/fontawesome/webfonts/fa-solid-900.subset.ttf is excluded by !**/*.ttf
  • public/assets/vendor/fontawesome/webfonts/fa-solid-900.subset.woff2 is excluded by !**/*.woff2
  • public/assets/vendor/swiper/swiper-bundle.min.js is excluded by !**/*.min.js
  • public/assets/vendor/tinymce/icons/default/icons.min.js is excluded by !**/*.min.js
  • public/assets/vendor/tinymce/models/dom/model.min.js is excluded by !**/*.min.js
  • public/assets/vendor/tinymce/plugins/accordion/plugin.min.js is excluded by !**/*.min.js
  • public/assets/vendor/tinymce/plugins/advlist/plugin.min.js is excluded by !**/*.min.js
  • public/assets/vendor/tinymce/plugins/anchor/plugin.min.js is excluded by !**/*.min.js
  • public/assets/vendor/tinymce/plugins/autolink/plugin.min.js is excluded by !**/*.min.js
  • public/assets/vendor/tinymce/plugins/autoresize/plugin.min.js is excluded by !**/*.min.js
  • public/assets/vendor/tinymce/plugins/autosave/plugin.min.js is excluded by !**/*.min.js
  • public/assets/vendor/tinymce/plugins/charmap/plugin.min.js is excluded by !**/*.min.js
  • public/assets/vendor/tinymce/plugins/code/plugin.min.js is excluded by !**/*.min.js
  • public/assets/vendor/tinymce/plugins/codesample/plugin.min.js is excluded by !**/*.min.js
  • public/assets/vendor/tinymce/plugins/directionality/plugin.min.js is excluded by !**/*.min.js
  • public/assets/vendor/tinymce/plugins/emoticons/plugin.min.js is excluded by !**/*.min.js
  • public/assets/vendor/tinymce/plugins/fullscreen/plugin.min.js is excluded by !**/*.min.js
  • public/assets/vendor/tinymce/plugins/help/plugin.min.js is excluded by !**/*.min.js
  • public/assets/vendor/tinymce/plugins/image/plugin.min.js is excluded by !**/*.min.js
  • public/assets/vendor/tinymce/plugins/importcss/plugin.min.js is excluded by !**/*.min.js
  • public/assets/vendor/tinymce/plugins/insertdatetime/plugin.min.js is excluded by !**/*.min.js
  • public/assets/vendor/tinymce/plugins/link/plugin.min.js is excluded by !**/*.min.js
  • public/assets/vendor/tinymce/plugins/lists/plugin.min.js is excluded by !**/*.min.js
  • public/assets/vendor/tinymce/plugins/media/plugin.min.js is excluded by !**/*.min.js
  • public/assets/vendor/tinymce/plugins/nonbreaking/plugin.min.js is excluded by !**/*.min.js
  • public/assets/vendor/tinymce/plugins/pagebreak/plugin.min.js is excluded by !**/*.min.js
  • public/assets/vendor/tinymce/plugins/preview/plugin.min.js is excluded by !**/*.min.js
  • public/assets/vendor/tinymce/plugins/quickbars/plugin.min.js is excluded by !**/*.min.js
  • public/assets/vendor/tinymce/plugins/save/plugin.min.js is excluded by !**/*.min.js
  • public/assets/vendor/tinymce/plugins/searchreplace/plugin.min.js is excluded by !**/*.min.js
  • public/assets/vendor/tinymce/plugins/table/plugin.min.js is excluded by !**/*.min.js
  • public/assets/vendor/tinymce/plugins/template/plugin.min.js is excluded by !**/*.min.js
  • public/assets/vendor/tinymce/plugins/visualblocks/plugin.min.js is excluded by !**/*.min.js
  • public/assets/vendor/tinymce/plugins/visualchars/plugin.min.js is excluded by !**/*.min.js
  • public/assets/vendor/tinymce/plugins/wordcount/plugin.min.js is excluded by !**/*.min.js
  • public/assets/vendor/tinymce/themes/silver/theme.min.js is excluded by !**/*.min.js
  • public/assets/vendor/tinymce/tinymce.min.js is excluded by !**/*.min.js
📒 Files selected for processing (238)
  • .dockerignore
  • DOCKER.md
  • Dockerfile
  • app/Controllers/Admin/AlbumsController.php
  • app/Controllers/Admin/MediaController.php
  • app/Controllers/Frontend/MediaController.php
  • app/Middlewares/CsrfMiddleware.php
  • app/Services/ProtectedMediaStorage.php
  • app/Services/UploadService.php
  • app/Services/VariantMaintenanceService.php
  • app/Views/admin/pages/about.twig
  • app/Views/admin/pages/cookie.twig
  • app/Views/admin/pages/home.twig
  • app/Views/admin/pages/license.twig
  • app/Views/admin/pages/privacy.twig
  • database/migrations/migrate_1.4.19_mysql.sql
  • database/migrations/migrate_1.4.19_sqlite.sql
  • database/schema.mysql.sql
  • database/schema.sqlite.sql
  • database/template.sqlite
  • docker-compose.yml
  • docker/entrypoint.sh
  • package.json
  • phpstan.neon.dist
  • public/assets/.vite/manifest.json
  • public/assets/admin-CoDgvQhn.js
  • public/assets/admin-DMLC04MW.css
  • public/assets/admin-DhmGQElJ.css
  • public/assets/admin-R8nArFSG.js
  • public/assets/admin-app-BIVRj_IL.css
  • public/assets/admin-app-nqbdg2Fu.css
  • public/assets/admin-app.css
  • public/assets/app-BBeNoDm8.css
  • public/assets/app-CnQmMgwn.css
  • public/assets/app.css
  • public/assets/vendor/fontawesome/css/frontend-subset.css
  • public/assets/vendor/swiper/swiper-bundle.min.css
  • public/assets/vendor/tinymce/bower.json
  • public/assets/vendor/tinymce/composer.json
  • public/assets/vendor/tinymce/icons/default/icons.js
  • public/assets/vendor/tinymce/license.txt
  • public/assets/vendor/tinymce/models/dom/model.js
  • public/assets/vendor/tinymce/notices.txt
  • public/assets/vendor/tinymce/package.json
  • public/assets/vendor/tinymce/plugins/accordion/plugin.js
  • public/assets/vendor/tinymce/plugins/advlist/plugin.js
  • public/assets/vendor/tinymce/plugins/anchor/plugin.js
  • public/assets/vendor/tinymce/plugins/autolink/plugin.js
  • public/assets/vendor/tinymce/plugins/autoresize/plugin.js
  • public/assets/vendor/tinymce/plugins/autosave/plugin.js
  • public/assets/vendor/tinymce/plugins/charmap/plugin.js
  • public/assets/vendor/tinymce/plugins/code/plugin.js
  • public/assets/vendor/tinymce/plugins/codesample/plugin.js
  • public/assets/vendor/tinymce/plugins/directionality/plugin.js
  • public/assets/vendor/tinymce/plugins/emoticons/plugin.js
  • public/assets/vendor/tinymce/plugins/fullscreen/plugin.js
  • public/assets/vendor/tinymce/plugins/help/js/i18n/keynav/ar.js
  • public/assets/vendor/tinymce/plugins/help/js/i18n/keynav/bg-BG.js
  • public/assets/vendor/tinymce/plugins/help/js/i18n/keynav/bg_BG.js
  • public/assets/vendor/tinymce/plugins/help/js/i18n/keynav/ca.js
  • public/assets/vendor/tinymce/plugins/help/js/i18n/keynav/cs.js
  • public/assets/vendor/tinymce/plugins/help/js/i18n/keynav/da.js
  • public/assets/vendor/tinymce/plugins/help/js/i18n/keynav/de.js
  • public/assets/vendor/tinymce/plugins/help/js/i18n/keynav/el.js
  • public/assets/vendor/tinymce/plugins/help/js/i18n/keynav/en.js
  • public/assets/vendor/tinymce/plugins/help/js/i18n/keynav/es.js
  • public/assets/vendor/tinymce/plugins/help/js/i18n/keynav/eu.js
  • public/assets/vendor/tinymce/plugins/help/js/i18n/keynav/fa.js
  • public/assets/vendor/tinymce/plugins/help/js/i18n/keynav/fi.js
  • public/assets/vendor/tinymce/plugins/help/js/i18n/keynav/fr-FR.js
  • public/assets/vendor/tinymce/plugins/help/js/i18n/keynav/fr_FR.js
  • public/assets/vendor/tinymce/plugins/help/js/i18n/keynav/he-IL.js
  • public/assets/vendor/tinymce/plugins/help/js/i18n/keynav/he_IL.js
  • public/assets/vendor/tinymce/plugins/help/js/i18n/keynav/hi.js
  • public/assets/vendor/tinymce/plugins/help/js/i18n/keynav/hr.js
  • public/assets/vendor/tinymce/plugins/help/js/i18n/keynav/hu-HU.js
  • public/assets/vendor/tinymce/plugins/help/js/i18n/keynav/hu_HU.js
  • public/assets/vendor/tinymce/plugins/help/js/i18n/keynav/id.js
  • public/assets/vendor/tinymce/plugins/help/js/i18n/keynav/it.js
  • public/assets/vendor/tinymce/plugins/help/js/i18n/keynav/ja.js
  • public/assets/vendor/tinymce/plugins/help/js/i18n/keynav/kk.js
  • public/assets/vendor/tinymce/plugins/help/js/i18n/keynav/ko-KR.js
  • public/assets/vendor/tinymce/plugins/help/js/i18n/keynav/ko_KR.js
  • public/assets/vendor/tinymce/plugins/help/js/i18n/keynav/ms.js
  • public/assets/vendor/tinymce/plugins/help/js/i18n/keynav/nb-NO.js
  • public/assets/vendor/tinymce/plugins/help/js/i18n/keynav/nb_NO.js
  • public/assets/vendor/tinymce/plugins/help/js/i18n/keynav/nl.js
  • public/assets/vendor/tinymce/plugins/help/js/i18n/keynav/pl.js
  • public/assets/vendor/tinymce/plugins/help/js/i18n/keynav/pt-BR.js
  • public/assets/vendor/tinymce/plugins/help/js/i18n/keynav/pt-PT.js
  • public/assets/vendor/tinymce/plugins/help/js/i18n/keynav/pt_BR.js
  • public/assets/vendor/tinymce/plugins/help/js/i18n/keynav/pt_PT.js
  • public/assets/vendor/tinymce/plugins/help/js/i18n/keynav/ro.js
  • public/assets/vendor/tinymce/plugins/help/js/i18n/keynav/ru.js
  • public/assets/vendor/tinymce/plugins/help/js/i18n/keynav/sk.js
  • public/assets/vendor/tinymce/plugins/help/js/i18n/keynav/sl-SI.js
  • public/assets/vendor/tinymce/plugins/help/js/i18n/keynav/sl_SI.js
  • public/assets/vendor/tinymce/plugins/help/js/i18n/keynav/sv-SE.js
  • public/assets/vendor/tinymce/plugins/help/js/i18n/keynav/sv_SE.js
  • public/assets/vendor/tinymce/plugins/help/js/i18n/keynav/th-TH.js
  • public/assets/vendor/tinymce/plugins/help/js/i18n/keynav/th_TH.js
  • public/assets/vendor/tinymce/plugins/help/js/i18n/keynav/tr.js
  • public/assets/vendor/tinymce/plugins/help/js/i18n/keynav/uk.js
  • public/assets/vendor/tinymce/plugins/help/js/i18n/keynav/vi.js
  • public/assets/vendor/tinymce/plugins/help/js/i18n/keynav/zh-CN.js
  • public/assets/vendor/tinymce/plugins/help/js/i18n/keynav/zh-TW.js
  • public/assets/vendor/tinymce/plugins/help/js/i18n/keynav/zh_CN.js
  • public/assets/vendor/tinymce/plugins/help/js/i18n/keynav/zh_TW.js
  • public/assets/vendor/tinymce/plugins/help/plugin.js
  • public/assets/vendor/tinymce/plugins/image/plugin.js
  • public/assets/vendor/tinymce/plugins/importcss/plugin.js
  • public/assets/vendor/tinymce/plugins/insertdatetime/plugin.js
  • public/assets/vendor/tinymce/plugins/link/plugin.js
  • public/assets/vendor/tinymce/plugins/lists/plugin.js
  • public/assets/vendor/tinymce/plugins/media/plugin.js
  • public/assets/vendor/tinymce/plugins/nonbreaking/plugin.js
  • public/assets/vendor/tinymce/plugins/pagebreak/plugin.js
  • public/assets/vendor/tinymce/plugins/preview/plugin.js
  • public/assets/vendor/tinymce/plugins/quickbars/plugin.js
  • public/assets/vendor/tinymce/plugins/save/plugin.js
  • public/assets/vendor/tinymce/plugins/searchreplace/plugin.js
  • public/assets/vendor/tinymce/plugins/table/plugin.js
  • public/assets/vendor/tinymce/plugins/template/index.js
  • public/assets/vendor/tinymce/plugins/template/plugin.js
  • public/assets/vendor/tinymce/plugins/visualblocks/plugin.js
  • public/assets/vendor/tinymce/plugins/visualchars/plugin.js
  • public/assets/vendor/tinymce/plugins/wordcount/plugin.js
  • public/assets/vendor/tinymce/skins/content/dark/content.css
  • public/assets/vendor/tinymce/skins/content/dark/content.js
  • public/assets/vendor/tinymce/skins/content/dark/content.min.css
  • public/assets/vendor/tinymce/skins/content/dark/content.min.ts
  • public/assets/vendor/tinymce/skins/content/dark/content.ts
  • public/assets/vendor/tinymce/skins/content/default/content.css
  • public/assets/vendor/tinymce/skins/content/default/content.js
  • public/assets/vendor/tinymce/skins/content/default/content.min.css
  • public/assets/vendor/tinymce/skins/content/default/content.min.ts
  • public/assets/vendor/tinymce/skins/content/default/content.ts
  • public/assets/vendor/tinymce/skins/content/document/content.css
  • public/assets/vendor/tinymce/skins/content/document/content.js
  • public/assets/vendor/tinymce/skins/content/document/content.min.css
  • public/assets/vendor/tinymce/skins/content/document/content.min.ts
  • public/assets/vendor/tinymce/skins/content/document/content.ts
  • public/assets/vendor/tinymce/skins/content/tinymce-5-dark/content.css
  • public/assets/vendor/tinymce/skins/content/tinymce-5-dark/content.js
  • public/assets/vendor/tinymce/skins/content/tinymce-5-dark/content.min.css
  • public/assets/vendor/tinymce/skins/content/tinymce-5-dark/content.min.ts
  • public/assets/vendor/tinymce/skins/content/tinymce-5-dark/content.ts
  • public/assets/vendor/tinymce/skins/content/tinymce-5/content.css
  • public/assets/vendor/tinymce/skins/content/tinymce-5/content.js
  • public/assets/vendor/tinymce/skins/content/tinymce-5/content.min.css
  • public/assets/vendor/tinymce/skins/content/tinymce-5/content.min.ts
  • public/assets/vendor/tinymce/skins/content/tinymce-5/content.ts
  • public/assets/vendor/tinymce/skins/content/writer/content.css
  • public/assets/vendor/tinymce/skins/content/writer/content.js
  • public/assets/vendor/tinymce/skins/content/writer/content.min.css
  • public/assets/vendor/tinymce/skins/content/writer/content.min.ts
  • public/assets/vendor/tinymce/skins/content/writer/content.ts
  • public/assets/vendor/tinymce/skins/ui/oxide-dark/content.css
  • public/assets/vendor/tinymce/skins/ui/oxide-dark/content.inline.css
  • public/assets/vendor/tinymce/skins/ui/oxide-dark/content.inline.js
  • public/assets/vendor/tinymce/skins/ui/oxide-dark/content.inline.min.css
  • public/assets/vendor/tinymce/skins/ui/oxide-dark/content.inline.min.ts
  • public/assets/vendor/tinymce/skins/ui/oxide-dark/content.inline.ts
  • public/assets/vendor/tinymce/skins/ui/oxide-dark/content.js
  • public/assets/vendor/tinymce/skins/ui/oxide-dark/content.min.css
  • public/assets/vendor/tinymce/skins/ui/oxide-dark/content.min.ts
  • public/assets/vendor/tinymce/skins/ui/oxide-dark/content.ts
  • public/assets/vendor/tinymce/skins/ui/oxide-dark/skin.css
  • public/assets/vendor/tinymce/skins/ui/oxide-dark/skin.js
  • public/assets/vendor/tinymce/skins/ui/oxide-dark/skin.min.css
  • public/assets/vendor/tinymce/skins/ui/oxide-dark/skin.min.ts
  • public/assets/vendor/tinymce/skins/ui/oxide-dark/skin.shadowdom.js
  • public/assets/vendor/tinymce/skins/ui/oxide-dark/skin.shadowdom.min.ts
  • public/assets/vendor/tinymce/skins/ui/oxide-dark/skin.shadowdom.ts
  • public/assets/vendor/tinymce/skins/ui/oxide-dark/skin.ts
  • public/assets/vendor/tinymce/skins/ui/oxide/content.css
  • public/assets/vendor/tinymce/skins/ui/oxide/content.inline.css
  • public/assets/vendor/tinymce/skins/ui/oxide/content.inline.js
  • public/assets/vendor/tinymce/skins/ui/oxide/content.inline.min.css
  • public/assets/vendor/tinymce/skins/ui/oxide/content.inline.min.ts
  • public/assets/vendor/tinymce/skins/ui/oxide/content.inline.ts
  • public/assets/vendor/tinymce/skins/ui/oxide/content.js
  • public/assets/vendor/tinymce/skins/ui/oxide/content.min.css
  • public/assets/vendor/tinymce/skins/ui/oxide/content.min.ts
  • public/assets/vendor/tinymce/skins/ui/oxide/content.ts
  • public/assets/vendor/tinymce/skins/ui/oxide/skin.css
  • public/assets/vendor/tinymce/skins/ui/oxide/skin.js
  • public/assets/vendor/tinymce/skins/ui/oxide/skin.min.css
  • public/assets/vendor/tinymce/skins/ui/oxide/skin.min.ts
  • public/assets/vendor/tinymce/skins/ui/oxide/skin.shadowdom.js
  • public/assets/vendor/tinymce/skins/ui/oxide/skin.shadowdom.min.ts
  • public/assets/vendor/tinymce/skins/ui/oxide/skin.shadowdom.ts
  • public/assets/vendor/tinymce/skins/ui/oxide/skin.ts
  • public/assets/vendor/tinymce/skins/ui/tinymce-5-dark/content.css
  • public/assets/vendor/tinymce/skins/ui/tinymce-5-dark/content.inline.css
  • public/assets/vendor/tinymce/skins/ui/tinymce-5-dark/content.inline.js
  • public/assets/vendor/tinymce/skins/ui/tinymce-5-dark/content.inline.min.css
  • public/assets/vendor/tinymce/skins/ui/tinymce-5-dark/content.inline.min.ts
  • public/assets/vendor/tinymce/skins/ui/tinymce-5-dark/content.inline.ts
  • public/assets/vendor/tinymce/skins/ui/tinymce-5-dark/content.js
  • public/assets/vendor/tinymce/skins/ui/tinymce-5-dark/content.min.css
  • public/assets/vendor/tinymce/skins/ui/tinymce-5-dark/content.min.ts
  • public/assets/vendor/tinymce/skins/ui/tinymce-5-dark/content.ts
  • public/assets/vendor/tinymce/skins/ui/tinymce-5-dark/skin.css
  • public/assets/vendor/tinymce/skins/ui/tinymce-5-dark/skin.js
  • public/assets/vendor/tinymce/skins/ui/tinymce-5-dark/skin.min.css
  • public/assets/vendor/tinymce/skins/ui/tinymce-5-dark/skin.min.ts
  • public/assets/vendor/tinymce/skins/ui/tinymce-5-dark/skin.shadowdom.js
  • public/assets/vendor/tinymce/skins/ui/tinymce-5-dark/skin.shadowdom.min.ts
  • public/assets/vendor/tinymce/skins/ui/tinymce-5-dark/skin.shadowdom.ts
  • public/assets/vendor/tinymce/skins/ui/tinymce-5-dark/skin.ts
  • public/assets/vendor/tinymce/skins/ui/tinymce-5/content.css
  • public/assets/vendor/tinymce/skins/ui/tinymce-5/content.inline.css
  • public/assets/vendor/tinymce/skins/ui/tinymce-5/content.inline.js
  • public/assets/vendor/tinymce/skins/ui/tinymce-5/content.inline.min.css
  • public/assets/vendor/tinymce/skins/ui/tinymce-5/content.inline.min.ts
  • public/assets/vendor/tinymce/skins/ui/tinymce-5/content.inline.ts
  • public/assets/vendor/tinymce/skins/ui/tinymce-5/content.js
  • public/assets/vendor/tinymce/skins/ui/tinymce-5/content.min.css
  • public/assets/vendor/tinymce/skins/ui/tinymce-5/content.min.ts
  • public/assets/vendor/tinymce/skins/ui/tinymce-5/content.ts
  • public/assets/vendor/tinymce/skins/ui/tinymce-5/skin.css
  • public/assets/vendor/tinymce/skins/ui/tinymce-5/skin.js
  • public/assets/vendor/tinymce/skins/ui/tinymce-5/skin.min.css
  • public/assets/vendor/tinymce/skins/ui/tinymce-5/skin.min.ts
  • public/assets/vendor/tinymce/skins/ui/tinymce-5/skin.shadowdom.js
  • public/assets/vendor/tinymce/skins/ui/tinymce-5/skin.shadowdom.min.ts
  • public/assets/vendor/tinymce/skins/ui/tinymce-5/skin.shadowdom.ts
  • public/assets/vendor/tinymce/skins/ui/tinymce-5/skin.ts
  • public/assets/vendor/tinymce/themes/silver/theme.js
  • public/assets/vendor/tinymce/tinymce.d.ts
  • public/assets/vendor/tinymce/tinymce.js
  • public/installer.php
  • public/sw.js
  • resources/admin.js
  • resources/stubs/jcupitt-vips.php
  • tests/Database/MigrationsTest.php
  • version.json

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/media-docker-security-hardening

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Correctness:
- MediaController 304 responses now carry Content-Type: PHP-level header
  middleware (CsrfMiddleware's image guard) must see the revalidation as an
  image, or the session-bound X-CSRF-Token gets folded into publicly-cached
  entries by revalidating shared caches. Denial responses (blur/placeholder
  under the sharp URL) unified behind withDenialHeaders(), which also adds
  the X-Robots-Tag the sibling denial paths already sent.
- sw.js cache-first now honours Cache-Control: no-store — it used to cache
  the denial blur under the sharp variant URL and serve it forever, undoing
  the server-side fix for every PWA client.
- The five admin page templates that call tinymce.init() directly now pass
  license_key 'gpl' (TinyMCE 7+ requirement), matching resources/admin.js.
- The installer no longer renders the CIMAISE_DB_PASSWORD into the pre-auth
  HTML (any visitor could read it during the install window). The password
  is applied server-side on submit, and only when the submitted host, user
  and database exactly match the env-provided coordinates — so the fallback
  cannot be replayed against an attacker-chosen host. The prefill notice
  also survives a failed first attempt (e.g. MySQL still initializing).
- UploadService::canGenerateFormat() now mirrors the real encoder chain,
  including the CIMAISE_DISABLE_IMAGICK opt-out that raw ImageEngine
  capabilities ignore; VariantMaintenanceService uses it, so maintenance
  can never count a format the generator cannot emit.
- deleteOriginalIfUnreferenced(): blank legacy paths no longer count as
  failed deletions, and originals are resolved against the same candidate
  locations generateVariantsForImage() uses (legacy /media/originals rows).
- Docker entrypoint translation seed is checksum-guarded: admin-modified
  packs (Texts import with save-to-file) are preserved across restarts,
  seeding failures are reported instead of swallowed, and the awk probe no
  longer aborts the set -e entrypoint on first boot.

Cleanup / performance:
- resizeWithGdWebp/resizeWithGdAvif collapsed into one resizeWithGd()
  (plus AVIF speed 7 — libaom's default is drastically slower for a few
  percent of size on GD-only hosts).
- Protected-album relocation loop extracted to
  ProtectedMediaStorage::relocateAllProtectedAlbums(), shared by the
  one-time quarantine and the daily reconciliation.
- Migration 1.4.19: indexes on images.original_path and
  image_variants.path — both were probed per-file in delete loops and the
  daily reconciliation as full table scans. Schemas + template.sqlite
  regenerated, version bumped to 1.4.19.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant