diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml
index 7b41c36d6..9b1e5b5f9 100644
--- a/.github/workflows/deploy.yml
+++ b/.github/workflows/deploy.yml
@@ -431,6 +431,7 @@ jobs:
CHROMIUM_REQUIRE=1 BASE_URL=http://localhost:13581 node test-xss-333-pages-e2e.js 2>&1 | tee -a e2e-output.txt
CHROMIUM_REQUIRE=1 BASE_URL=http://localhost:13581 node test-xss-392-perf-e2e.js 2>&1 | tee -a e2e-output.txt
CHROMIUM_REQUIRE=1 BASE_URL=http://localhost:13581 node test-xss-368-home-links-e2e.js 2>&1 | tee -a e2e-output.txt
+ CHROMIUM_REQUIRE=1 BASE_URL=http://localhost:13581 node test-xss-429-home-footer-e2e.js 2>&1 | tee -a e2e-output.txt
BASE_URL=http://localhost:13581 node test-issue-1639-observers-sort-e2e.js 2>&1 | tee -a e2e-output.txt
CHROMIUM_REQUIRE=1 BASE_URL=http://localhost:13581 node test-issue-405-observers-listener-leak-e2e.js 2>&1 | tee -a e2e-output.txt
CHROMIUM_REQUIRE=1 BASE_URL=http://localhost:13581 node test-issue-1758-ng-filter-rerenders-e2e.js 2>&1 | tee -a e2e-output.txt
diff --git a/public/customize-v2.js b/public/customize-v2.js
index 45f044598..6bae00fc3 100644
--- a/public/customize-v2.js
+++ b/public/customize-v2.js
@@ -68,6 +68,8 @@
function _setBrandLogoUrl(url, alt) {
var node = document.querySelector('.nav-brand .brand-logo');
if (!node) return;
+ // #429 — a URL the shared scheme check rejects is treated as cleared.
+ if (url && !_brandUrlOk(url)) url = '';
if (url) {
if (node.tagName.toLowerCase() === 'img') {
node.setAttribute('src', url);
@@ -97,6 +99,17 @@
if (alt != null) node.setAttribute('alt', alt);
}
}
+ // #429 — branding URLs are customization content (server theme, stored
+ // overrides, theme import), so they get the scheme check roles.js exports
+ // for miniMarkdown and Home footer links, in its image-sink form. Fails
+ // closed when roles.js is not loaded.
+ function _brandUrlOk(url) {
+ return typeof window.isSafeImageUrl === 'function' && window.isSafeImageUrl(url);
+ }
+ function _setFaviconUrl(url) {
+ var link = document.querySelector('link[rel="icon"]');
+ if (link && url && _brandUrlOk(url)) link.href = url;
+ }
function _setBrandAlt(alt) {
var node = document.querySelector('.nav-brand .brand-logo');
if (!node) return;
@@ -795,15 +808,8 @@
var brandEl = document.querySelector('.brand-text');
if (brandEl) brandEl.textContent = br.siteName;
}
- if (br.logoUrl) {
- _setBrandLogoUrl(br.logoUrl, br.siteName || null);
- var iconEl = document.querySelector('.brand-icon');
- if (iconEl) iconEl.innerHTML = '';
- }
- if (br.faviconUrl) {
- var fav = document.querySelector('link[rel="icon"]');
- if (fav) fav.href = br.faviconUrl;
- }
+ if (br.logoUrl) _setBrandLogoUrl(br.logoUrl, br.siteName || null);
+ if (br.faviconUrl) _setFaviconUrl(br.faviconUrl);
// #1518 — apply validated homeUrl override to .nav-brand[href].
// Always call: invalid/empty restores '#/' so a cleared override visibly
// reverts. Validator rejects javascript:/data:/etc to prevent XSS.
@@ -1301,7 +1307,7 @@
function _renderBranding() {
var eff = _getEffective();
var b = eff.branding || {};
- var logoPreview = b.logoUrl ? '
' : '';
+ var logoPreview = b.logoUrl && _brandUrlOk(b.logoUrl) ? '
' : '';
return '