[object Object]

← back to Interiordesignershowroom

snapshot before deploy: in-tree security headers (nosniff/frame/referrer/permissions, drop x-powered-by) + don't leak raw error to /api/render client

c34917c82012da8026a0295f910e5c0fad99afce · 2026-08-03 11:13:46 -0700 · steve

Files touched

Diff

commit c34917c82012da8026a0295f910e5c0fad99afce
Author: steve <steve@designerwallcoverings.com>
Date:   Mon Aug 3 11:13:46 2026 -0700

    snapshot before deploy: in-tree security headers (nosniff/frame/referrer/permissions, drop x-powered-by) + don't leak raw error to /api/render client
---
 server.js | 16 +++++++++++++++-
 1 file changed, 15 insertions(+), 1 deletion(-)

diff --git a/server.js b/server.js
index ed80382..70da6cc 100644
--- a/server.js
+++ b/server.js
@@ -13,6 +13,20 @@ const COLS = require('./lib/cols');
 const app = express();
 const PORT = process.env.PORT || 9820;
 
+// Security headers on every response (no dependency). A strict Content-Security-Policy
+// is deliberately DEFERRED: the storefront still emits inline onerror/style handlers that
+// a CSP without 'unsafe-inline' would break — that's a separate refactor. These four are
+// non-breaking and close the obvious gaps (MIME sniffing, clickjacking, referrer leakage,
+// unused powerful features). Also stop advertising the framework.
+app.disable('x-powered-by');
+app.use((_req, res, next) => {
+  res.set('X-Content-Type-Options', 'nosniff');
+  res.set('X-Frame-Options', 'SAMEORIGIN');
+  res.set('Referrer-Policy', 'strict-origin-when-cross-origin');
+  res.set('Permissions-Policy', 'camera=(), microphone=(), geolocation=(), browsing-topics=()');
+  next();
+});
+
 app.use(express.json({ limit: '256kb' }));
 app.use('/css', express.static(path.join(__dirname, 'public/css')));
 app.use('/js', express.static(path.join(__dirname, 'public/js')));
@@ -482,7 +496,7 @@ app.post('/api/render', async (req, res) => {
     // return the products in the room so the right panel can show them (shoppable)
     const roomProducts = products.map((p) => ({ id: p.id, title: p.title, image_url: p.image_url, price: p.price, sale_price: p.sale_price, advertiser: p.advertiser, brand: p.brand }));
     res.json({ ...out, cost, hotspots: loc.hotspots, products: roomProducts });
-  } catch (e) { console.error('[render]', e.message); res.status(500).json({ error: e.message }); }
+  } catch (e) { console.error('[render]', e.message); res.status(500).json({ error: 'Render failed. Please try again.' }); }
 });
 
 app.get('/build', (_req, res) => {

← 81d733e refine: shared fmtStamp (storefront+admin, NaN/future-safe),  ·  back to Interiordesignershowroom  ·  security: response headers + non-breaking CSP (frame-ancesto 1bd3992 →