[object Object]

← back to Dw Domain Fleet

shared/render.js: scrub vendor names from tag-chips fleet-wide (closes audit 2026-05-28 task#53 HIGH finding). Add scrubTags() helper with 47-vendor denylist (mirrors _shared/sku-redact.js + adds Kravet, de Gournay, Romo, Farrow & Ball, Colefax & Fowler, Sanderson, Morris & Co, Osborne & Little — all surfaced by the audit or missing from the prior denylist). Apply at both leak points: card data-tags attribute (feeds quick-view modal chips) + productPage <span> chip strip. Verified: 7 audit-leaked vendors filtered (Brunschwig & Fils, Kravet, Schumacher, Maya Romanoff, Lee Jofa, de Gournay, Romo), house brands preserved (Designer Wallcoverings, Hollywood). NOT YET DEPLOYED — awaiting Steve sign-off + drift-aware deploy plan.

82e096808f12038e6987287e4190b2168af24a3e · 2026-05-28 16:58:20 -0700 · Steve Abrams

Files touched

Diff

commit 82e096808f12038e6987287e4190b2168af24a3e
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Thu May 28 16:58:20 2026 -0700

    shared/render.js: scrub vendor names from tag-chips fleet-wide (closes audit 2026-05-28 task#53 HIGH finding). Add scrubTags() helper with 47-vendor denylist (mirrors _shared/sku-redact.js + adds Kravet, de Gournay, Romo, Farrow & Ball, Colefax & Fowler, Sanderson, Morris & Co, Osborne & Little — all surfaced by the audit or missing from the prior denylist). Apply at both leak points: card data-tags attribute (feeds quick-view modal chips) + productPage <span> chip strip. Verified: 7 audit-leaked vendors filtered (Brunschwig & Fils, Kravet, Schumacher, Maya Romanoff, Lee Jofa, de Gournay, Romo), house brands preserved (Designer Wallcoverings, Hollywood). NOT YET DEPLOYED — awaiting Steve sign-off + drift-aware deploy plan.
---
 shared/render.js | 42 ++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 40 insertions(+), 2 deletions(-)

diff --git a/shared/render.js b/shared/render.js
index f19cb8a..425603c 100644
--- a/shared/render.js
+++ b/shared/render.js
@@ -24,6 +24,44 @@ function productSlug(p) {
   return h.replace(/wallpapers/gi, 'wallcoverings').replace(/wallpaper/gi, 'wallcovering');
 }
 
+/* scrubTags — drop any tag carrying a 3rd-party vendor token before rendering.
+ * Mirrors the VENDORS list in _shared/sku-redact.js (which only covers .ven/.r-sku
+ * card-meta lines). Tag-chip surfaces (data-tags attribute on cards + productPage
+ * <span> chip strip) were leaking "Brunschwig & Fils", "Kravet", "Lee Jofa" etc
+ * straight from p.tags[] to the rendered DOM (audited 2026-05-28). This filter
+ * runs at the render boundary so both the data-tags attribute and the
+ * productPage chips see the scrubbed list. House brands (Designer Wallcoverings,
+ * Hollywood, Phillipe Romano, Retro Walls, Maison Lustre) are NEVER scrubbed —
+ * they're not on the denylist. Read-mostly source-of-truth: keep VENDORS in sync
+ * with _shared/sku-redact.js; if you add a vendor there, add it here too. */
+const VENDORS_DENYLIST = [
+  'wolf gordon', 'nina campbell', 'jeffrey stevens', 'versace',
+  'arte international', 'lee jofa modern', 'lee jofa', 'innovations usa',
+  'innovations', 'koroseal', 'schumacher', 'candice olson', 'thibaut',
+  'maya romanoff', 'scalamandre', 'dedar', 'carnegie', 'cole and son',
+  'kravet', 'clarke and clarke', 'brunschwig', 'gp j baker', 'sandberg',
+  'designers guild', 'graham and brown', 'harlequin', 'andrew martin',
+  'ronald redding', 'blithfield', 'coordonne', 'fentucci', 'sister parish',
+  'phillip jeffries', 'fromental', 'westport', 'breegan', 'les ensembliers',
+  'roger thomas', 'stacy garcia', 'de gournay', 'romo', 'farrow and ball',
+  'colefax and fowler', 'sanderson', 'morris and co', 'osborne and little',
+];
+const VENDORS_DENYLIST_NORM = VENDORS_DENYLIST.map(s =>
+  String(s).toLowerCase().replace(/[^a-z0-9]+/g, '')
+);
+function hasVendorToken(s) {
+  const n = String(s == null ? '' : s).toLowerCase().replace(/[^a-z0-9]+/g, '');
+  if (!n) return false;
+  for (let i = 0; i < VENDORS_DENYLIST_NORM.length; i++) {
+    if (n.indexOf(VENDORS_DENYLIST_NORM[i]) >= 0) return true;
+  }
+  return false;
+}
+function scrubTags(tags) {
+  if (!Array.isArray(tags)) return [];
+  return tags.filter(t => t && !hasVendorToken(t));
+}
+
 const DW = 'https://designerwallcoverings.com';
 
 /* ---------- shared CSS ---------- */
@@ -254,7 +292,7 @@ function card(p) {
   const href = '/product/' + encodeURIComponent(productSlug(p));
   return `<div class="card" data-handle="${esc(p.handle)}"
     data-title="${esc(t)}" data-sku="${esc(p.sku||p.handle)}"
-    data-img="${esc(p.image_url)}" data-tags="${esc((p.tags||[]).slice(0,12).join(', '))}"
+    data-img="${esc(p.image_url)}" data-tags="${esc(scrubTags(p.tags).slice(0,12).join(', '))}"
     data-price="${esc(p.price)}">
   <a class="card-link" href="${esc(href)}" aria-label="${esc(t)}">
     <div class="card-img" style="background-image:url('${esc(p.image_url)}');--card-bg:url('${esc(p.image_url)}')"></div>
@@ -535,7 +573,7 @@ function productPage(cfg, p, related) {
   const title = `${name} — ${sku} | ${cfg.siteName}`;
   const desc = `${name} (${sku}) — ${clean(cfg.tagline)} Available through ${cfg.siteName} and Designer Wallcoverings with a free memo sample and expert order hand-holding.`;
   const hx = dominantHex(p);
-  const chips = (p.tags || []).filter(Boolean).slice(0, 12)
+  const chips = scrubTags(p.tags).filter(Boolean).slice(0, 12)
     .map(t => `<span>${esc(t)}</span>`).join('');
   const productLd = {
     '@context':'https://schema.org','@type':'Product',

← af735a7 shared/render.js: reconcile prod drift — pull equalize toolb  ·  back to Dw Domain Fleet  ·  shared/render.js: scrub head-baked vendor from product title 661cbec →