← back to Naturaltextilewallpaper
render: route buy CTA + main-store anchor + card sample through /buy/:slug + close denylist gap (cowtan/designtex/knoll/brewster/york/fabricut/stout/hygge/designerwallcoverings); kill vendor-handle DOM leak (mirrors fleet a85eb02)
c2bd7bc11f3e90c719106ce1d461d1186c343722 · 2026-05-31 21:03:09 -0700 · Steve Abrams
Files touched
Diff
commit c2bd7bc11f3e90c719106ce1d461d1186c343722
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Sun May 31 21:03:09 2026 -0700
render: route buy CTA + main-store anchor + card sample through /buy/:slug + close denylist gap (cowtan/designtex/knoll/brewster/york/fabricut/stout/hygge/designerwallcoverings); kill vendor-handle DOM leak (mirrors fleet a85eb02)
---
server.js | 21 ++++++++++++++++++---
1 file changed, 18 insertions(+), 3 deletions(-)
diff --git a/server.js b/server.js
index 9a8c26d..dc19b22 100644
--- a/server.js
+++ b/server.js
@@ -47,6 +47,9 @@ const VENDORS_DENYLIST = [
'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',
+ // 2026-05-31 audit gap-close: these leaked even on scrubbed surfaces.
+ 'cowtan', 'designtex', 'knoll', 'brewster', 'york', 'fabricut', 'stout',
+ 'hygge', 'designerwallcoverings',
];
// sort longest-first so multi-word vendors strip before their abbreviations
// (e.g. "arte international" before bare "arte" → no "international" residue)
@@ -269,6 +272,17 @@ app.get('/api/facets', (req, res) => {
});
// --- Product detail page ------------------------------------------------
+// /buy/:slug — internal 302 to the main store. Resolves a vendor-redacted slug
+// to the real handle SERVER-SIDE so the raw vendor-bearing handle never appears
+// in customer-facing HTML (mirrors dw-domain-fleet a85eb02). ?sample → #sample.
+app.get('/buy/:slug', (req, res) => {
+ const key = req.params.slug;
+ const p = PRODUCTS.find(x => x.handle === key)
+ || PRODUCTS.find(x => redactVendorSlug(x.handle) === key);
+ if (!p) return res.status(404).send(page404());
+ res.redirect(302, `${DW}/products/${p.handle}` + (req.query.sample != null ? '#sample' : ''));
+});
+
app.get('/p/:handle', (req, res) => {
const key = req.params.handle;
const p = PRODUCTS.find(x => x.handle === key)
@@ -645,7 +659,7 @@ function renderIndex(q, material, sort) {
+ '<span class="card-title">'+escHtml(p.title)+'</span>'
+ priceStr
+ '</div>'
- + '<a href="https://designerwallcoverings.com/products/'+p.handle+'#sample" class="sample-cta" target="_blank" rel="noopener noreferrer" onclick="event.stopPropagation()">Order Memo Sample (free)</a>'
+ + '<a href="/buy/'+encodeURIComponent(p.slug||p.handle)+'?sample=1" class="sample-cta" target="_blank" rel="noopener noreferrer" onclick="event.stopPropagation()">Order Memo Sample (free)</a>'
+ '</a>';
}
@@ -735,8 +749,9 @@ function renderIndex(q, material, sort) {
}
function renderDetail(p, matLabel) {
- const dw_url = `${DW}/products/${p.handle}`;
- const sample_url = `${dw_url}#sample`;
+ const buySlug = encodeURIComponent(redactVendorSlug(p.handle));
+ const dw_url = `/buy/${buySlug}`;
+ const sample_url = `/buy/${buySlug}?sample=1`;
const images = p.images && p.images.length > 1 ? p.images : [p.image_url];
return headHTML(
`${p.title} — Free Memo Sample | Natural Textile Wallpaper`,
← f63b951 Add per-site favicon (kills /favicon.ico 404)
·
back to Naturaltextilewallpaper
·
Vendor-neutral image proxy via _shared/image-proxy: hero+det 2895d20 →