From edd5dcab708b8c92c68a90611cceade930c2f918 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 2 Jul 2026 14:48:39 +0000 Subject: [PATCH] Address code review: fix escaping, guard nested groups, clarify error message --- public/js/dashboard.js | 4 ++-- routes/api/v1/ingame.js | 4 ++++ routes/dashboard.js | 2 +- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/public/js/dashboard.js b/public/js/dashboard.js index a81928a..93fa45d 100644 --- a/public/js/dashboard.js +++ b/public/js/dashboard.js @@ -598,10 +598,10 @@ function renderLogsList(logs) { const li = document.createElement('li'); li.className = 'acl-entry'; const granted = !!log.granted; - const cards = log.cardNumbers ? `, cards: ${log.cardNumbers}` : ''; + const cards = log.cardNumbers ? `, cards: ${escapeHtml(log.cardNumbers)}` : ''; li.innerHTML = ` ${granted ? 'Granted' : 'Denied'} - ${escapeHtml(log.scannedAt)} — user: ${escapeHtml(log.userId || 'unknown')}${escapeHtml(cards)} + ${escapeHtml(log.scannedAt)} — user: ${escapeHtml(log.userId || 'unknown')}${cards} `; list.appendChild(li); }); diff --git a/routes/api/v1/ingame.js b/routes/api/v1/ingame.js index 38f1af7..d1ba8c7 100644 --- a/routes/api/v1/ingame.js +++ b/routes/api/v1/ingame.js @@ -127,6 +127,10 @@ router.get('/:placeId/:accessPointId/onScan', async (req, res) => { } const byGroup = {}; for (const member of members) { + // Groups can only contain direct credentials (types 0-4); the API rejects + // nested groups on write, but skip any type 5 here too as defense-in-depth + // against infinite recursion if the database is ever modified directly. + if (member.type === 5) continue; if (!byGroup[member.groupId]) byGroup[member.groupId] = []; byGroup[member.groupId].push(member); } diff --git a/routes/dashboard.js b/routes/dashboard.js index 1807a3d..c1301f9 100644 --- a/routes/dashboard.js +++ b/routes/dashboard.js @@ -407,7 +407,7 @@ router.post('/places/:placeId/access-groups/:groupId/members', loadOwnedPlace, l } // Groups cannot contain other groups, to avoid nested/circular resolution. if (parseInt(type, 10) === 5) { - return res.status(400).json({ success: false, message: "An access group cannot contain another access group" }); + return res.status(400).json({ success: false, message: "An access group cannot contain another access group, to prevent circular/nested resolution" }); } db.run(`INSERT INTO access_group_members (groupId, type, data) VALUES (?, ?, ?)`, [req.accessGroup.id, type, data], function (err) {