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) {