From 1149687e3ca837fa2796cdce835e455002e6774a Mon Sep 17 00:00:00 2001 From: goyban Date: Sun, 16 Aug 2026 12:42:14 +0000 Subject: [PATCH] Security hardening: escape names in result screens, drop tech-stack headers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Escape player names rendered via innerHTML on the hand-over and game-over score rows (defense-in-depth XSS; names are already capped at 16 chars) - app.disable('x-powered-by') to stop advertising Express - Add safe response headers: X-Content-Type-Options, X-Frame-Options, Referrer-Policy (no CSP — inline SW script + socket.io would need unsafe-inline) Co-Authored-By: Claude Opus 4.8 --- package.json | 2 +- public/app.js | 4 ++-- server.js | 11 +++++++++++ 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index f7cc3e3..86a9823 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "shelem", - "version": "1.1.6", + "version": "1.1.7", "description": "Shelem card game — multiplayer", "main": "server.js", "scripts": { diff --git a/public/app.js b/public/app.js index 8fafce9..4478ac8 100644 --- a/public/app.js +++ b/public/app.js @@ -1179,7 +1179,7 @@ function onHandOver(st) { const isWinner = mySeat >= 0 && teamOf(mySeat) === team && delta > 0; if (isWinner) row.classList.add('winner'); row.innerHTML = ` - ${names} + ${escHtml(names)} ${delta >= 0 ? '+' : ''}${delta} → ${st.scores[team]} `; @@ -1211,7 +1211,7 @@ function onGameOver(st) { .filter(Boolean).join(' & ') || `Team ${team + 1}`; const row = document.createElement('div'); row.className = 'gameover-row' + (winTeams.has(team) ? ' winner' : ''); - row.innerHTML = `${names}${st.scores[team]}`; + row.innerHTML = `${escHtml(names)}${st.scores[team]}`; scoresEl.appendChild(row); } diff --git a/server.js b/server.js index 7ae70a7..0323431 100644 --- a/server.js +++ b/server.js @@ -10,6 +10,17 @@ const bcrypt = require('bcryptjs'); const jwt = require('jsonwebtoken'); const app = express(); +app.disable('x-powered-by'); // don't advertise the tech stack + +// Safe security headers (no CSP here — inline SW script + socket.io would need +// 'unsafe-inline'/wss exceptions; Cloudflare already terminates TLS/HSTS) +app.use((_req, res, next) => { + res.setHeader('X-Content-Type-Options', 'nosniff'); + res.setHeader('X-Frame-Options', 'DENY'); + res.setHeader('Referrer-Policy', 'no-referrer'); + next(); +}); + const httpServer = http.createServer(app); let httpsServer = null;