← back to Prestige Car Wash
slideshow: sync sanitized deck (XSS-safe cover); render verified 6 slides
57d8ba013afeff1c2c6ce86d6e76b20db0331518 · 2026-07-26 08:52:49 -0700 · Steve Abrams
Files touched
Diff
commit 57d8ba013afeff1c2c6ce86d6e76b20db0331518
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Sun Jul 26 08:52:49 2026 -0700
slideshow: sync sanitized deck (XSS-safe cover); render verified 6 slides
---
public/slideshow.html | 18 +++++++++++++++++-
1 file changed, 17 insertions(+), 1 deletion(-)
diff --git a/public/slideshow.html b/public/slideshow.html
index 8b22147..0be5f04 100644
--- a/public/slideshow.html
+++ b/public/slideshow.html
@@ -53,6 +53,22 @@
var deck=document.getElementById('deck'), slides=[], idx=0;
function esc(s){var d=document.createElement('div');d.textContent=s==null?'':String(s);return d.innerHTML;}
function txt(el){return el?el.textContent.replace(/\s+/g,' ').trim():'';}
+ // Cover heads keep the deliverable's <br>/<span> styling but are whitelist-sanitized:
+ // never render raw innerHTML from a parsed deliverable (XSS-safe). Only text, <br>, <span>.
+ function sanitizeHead(el){
+ var out='', ns=el.childNodes, i;
+ for(i=0;i<ns.length;i++){
+ var n=ns[i];
+ if(n.nodeType===3){ out+=esc(n.nodeValue); }
+ else if(n.nodeType===1){
+ var t=n.tagName.toLowerCase();
+ if(t==='br'){ out+='<br>'; }
+ else if(t==='span'){ out+='<span>'+esc(n.textContent)+'</span>'; }
+ else { out+=esc(n.textContent); }
+ }
+ }
+ return out;
+ }
// The deliverable lives at /portal on template-based portals, or at / on the
// originals (car-wash, fantasea). Try /portal, fall back to /.
@@ -65,7 +81,7 @@
// Cover slide from the deliverable's hero (h1 + first hero paragraph) or title.
var h1=doc.querySelector('.hero h1, h1');
var kick=doc.querySelector('.hero .kick, .kick, .eyebrow');
- out.push({cover:true, kicker:txt(kick)||'Consulting Deliverable', head:(h1?h1.innerHTML:document.title), sub:txt(doc.querySelector('.hero p'))});
+ out.push({cover:true, kicker:txt(kick)||'Consulting Deliverable', head:(h1?sanitizeHead(h1):esc(document.title)), sub:txt(doc.querySelector('.hero p'))});
// One slide per section.
var secs=doc.querySelectorAll('section[id]');
if(!secs.length) secs=doc.querySelectorAll('section');
← c94229a slideshow: /slideshow deck + Present button (deck falls back
·
back to Prestige Car Wash
·
SEO: server-inject AutoWash/LocalBusiness JSON-LD from place cbe52eb →