From 2f38dd0120313081087ad0db678a70510e97057b Mon Sep 17 00:00:00 2001 From: Yannick Warnier Date: Mon, 9 Mar 2026 14:02:56 +0100 Subject: [PATCH 1/3] WYSIWYG: Fix loading of tiny-settings.js in Firefox (NS_ERROR_CORRUPTED_CONTENT) due to incorrect MIME type - refs BT#22100 --- src/CoreBundle/Controller/ThemeController.php | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/src/CoreBundle/Controller/ThemeController.php b/src/CoreBundle/Controller/ThemeController.php index 225bb5821ed..db63b0de066 100644 --- a/src/CoreBundle/Controller/ThemeController.php +++ b/src/CoreBundle/Controller/ThemeController.php @@ -306,10 +306,20 @@ private function streamFile(FilesystemOperator $filesystem, string $filePath): R fclose($in); }); - $mimeType = $filesystem->mimeType($filePath) ?: 'application/octet-stream'; - if (str_ends_with(strtolower($filePath), '.svg')) { - $mimeType = 'image/svg+xml'; - } + $ext = strtolower(pathinfo($filePath, PATHINFO_EXTENSION)); + $extMimeMap = [ + 'js' => 'application/javascript', + 'css' => 'text/css', + 'svg' => 'image/svg+xml', + 'png' => 'image/png', + 'jpg' => 'image/jpeg', + 'jpeg' => 'image/jpeg', + 'gif' => 'image/gif', + 'webp' => 'image/webp', + 'woff' => 'font/woff', + 'woff2' => 'font/woff2', + ]; + $mimeType = $extMimeMap[$ext] ?? ($filesystem->mimeType($filePath) ?: 'application/octet-stream'); $disposition = $response->headers->makeDisposition( ResponseHeaderBag::DISPOSITION_INLINE, From a7cd95ba0559c731af9f8f4801b051e95222c4ab Mon Sep 17 00:00:00 2001 From: Yannick Warnier Date: Tue, 21 Apr 2026 00:29:16 +0200 Subject: [PATCH 2/3] Internal: Second-guess the use of http, when https an be detected - refs BT#22100 --- assets/vue/main.js | 12 ++++++++++++ assets/vue/utils/fetch.js | 8 +++++++- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/assets/vue/main.js b/assets/vue/main.js index 40d291b3e79..7fe53307c77 100644 --- a/assets/vue/main.js +++ b/assets/vue/main.js @@ -263,6 +263,18 @@ installHttpErrors({ on500: (err) => console.error("Server error", err?.response?.data?.detail || "Server error"), }) +// Fix mixed-content caused by server-generated http:// URLs when behind a +// TLS-terminating reverse proxy whose IP is not listed in TRUSTED_PROXIES. +// If the page was loaded over HTTPS, force every absolute http:// request URL +// to use https:// so the browser does not block it as mixed content. +axios.interceptors.request.use((config) => { + if (window.location.protocol === "https:" && config.url && config.url.startsWith("http://")) { + config.url = config.url.replace(/^http:\/\//, "https://") + } + + return config +}) + // Add cid/sid/gid automatically to every axios request to /api/* axios.interceptors.request.use((config) => { const sp = new URLSearchParams(window.location.search) diff --git a/assets/vue/utils/fetch.js b/assets/vue/utils/fetch.js index c831e51a2b4..7c29992aeab 100644 --- a/assets/vue/utils/fetch.js +++ b/assets/vue/utils/fetch.js @@ -92,7 +92,13 @@ export default function (id, options = {}) { console.log("ready to fetch") - return global.fetch(new URL(id, entryPoint), options).then((response) => { + const url = new URL(id, entryPoint) + // Force the current page's protocol to prevent mixed-content errors caused by + // server-generated http:// IRIs or redirects when behind a TLS-terminating + // reverse proxy whose IP is not listed in TRUSTED_PROXIES. + url.protocol = window.location.protocol + + return global.fetch(url, options).then((response) => { console.log(response, "global.fetch") if (response.ok) { From 296fd931583c175063681df70fd3129650df870b Mon Sep 17 00:00:00 2001 From: Yannick Warnier Date: Tue, 21 Apr 2026 00:36:29 +0200 Subject: [PATCH 3/3] Internal: Remove use of absolute URL in Vue URL generation features to reduce risk of wrong protocol links - refs BT#22100 --- assets/vue/config/api.js | 1 - assets/vue/config/entrypoint.js | 2 +- assets/vue/services/languageService.js | 2 +- assets/vue/utils/fetch.js | 9 +++++---- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/assets/vue/config/api.js b/assets/vue/config/api.js index ae613c7c90d..428301743ef 100644 --- a/assets/vue/config/api.js +++ b/assets/vue/config/api.js @@ -4,7 +4,6 @@ import axios from "axios" * @type {axios.AxiosInstance} */ const instance = axios.create({ - baseURL: window.location.origin, headers: { Accept: "application/ld+json", }, diff --git a/assets/vue/config/entrypoint.js b/assets/vue/config/entrypoint.js index 01dab83ccc5..8571c61a1fe 100644 --- a/assets/vue/config/entrypoint.js +++ b/assets/vue/config/entrypoint.js @@ -1,3 +1,3 @@ -export const ENTRYPOINT = window.location.origin + "/api/" +export const ENTRYPOINT = "/api/" //export const ENTRYPOINT = process.env.APP_API_PLATFORM_URL; diff --git a/assets/vue/services/languageService.js b/assets/vue/services/languageService.js index 180039dd5fc..031a01e35dd 100644 --- a/assets/vue/services/languageService.js +++ b/assets/vue/services/languageService.js @@ -3,7 +3,7 @@ import { ENTRYPOINT } from "../config/entrypoint" const legalExtensions = { async findAllAvailable() { - const url = new URL(`${ENTRYPOINT}languages`) + const url = new URL(`${ENTRYPOINT}languages`, window.location.origin) url.searchParams.append("available", "true") try { const response = await fetch(url.toString()) diff --git a/assets/vue/utils/fetch.js b/assets/vue/utils/fetch.js index 7c29992aeab..61598487928 100644 --- a/assets/vue/utils/fetch.js +++ b/assets/vue/utils/fetch.js @@ -92,10 +92,11 @@ export default function (id, options = {}) { console.log("ready to fetch") - const url = new URL(id, entryPoint) - // Force the current page's protocol to prevent mixed-content errors caused by - // server-generated http:// IRIs or redirects when behind a TLS-terminating - // reverse proxy whose IP is not listed in TRUSTED_PROXIES. + // Resolve the entrypoint against the page origin at call time so ENTRYPOINT + // can be a relative path ("/api/"). Then force the page protocol to prevent + // mixed-content from server-generated http:// IRIs behind a reverse proxy. + const base = new URL(entryPoint, window.location.origin) + const url = new URL(id, base) url.protocol = window.location.protocol return global.fetch(url, options).then((response) => {