security: address audit findings C-4 through M-8

Critical:
- C-4: Add express-rate-limit to login (20 attempts/15min)
- C-5: Remove default credentials from LoginForm.js
- C-6: Add sandbox attribute to KB document iframe

High:
- H-2: Hard-fail on startup if SESSION_SECRET env var is missing
- H-6: Sanitize filenames in Content-Disposition headers
- H-7: Fix KB upload race condition — move file after DB insert succeeds
- H-8: Generate random admin password in setup.js instead of hardcoded
- H-9: Add rehype-sanitize to ReactMarkdown (requires npm install)

Medium:
- M-4: Fix loose equality (==) to strict (===) in users.js self-checks
- M-5: Add hostname format regex validation in compliance notes
- M-6: Fix vendor trim-before-validate in ivantiTodoQueue.js
- M-7: Sanitize original filename in compliance temp JSON
- M-8: Pull CSP frame-ancestors from CORS_ORIGINS env var

New dependencies needed:
- backend: express-rate-limit (npm install in root)
- frontend: rehype-sanitize (npm install in frontend/)
This commit is contained in:
jramos
2026-04-07 10:23:10 -06:00
parent 169a0d2337
commit 8a6a3485e9
11 changed files with 62 additions and 34 deletions

View File

@@ -124,12 +124,12 @@ function createUsersRouter(db, requireAuth, requireGroup, logAudit) {
}
// Prevent admin self-demotion
if (userId == req.user.id && group && group !== 'Admin') {
if (String(userId) === String(req.user.id) && group && group !== 'Admin') {
return res.status(400).json({ error: 'Cannot remove your own admin group' });
}
// Prevent self-deactivation
if (userId == req.user.id && is_active === false) {
if (String(userId) === String(req.user.id) && is_active === false) {
return res.status(400).json({ error: 'Cannot deactivate your own account' });
}
@@ -247,7 +247,7 @@ function createUsersRouter(db, requireAuth, requireGroup, logAudit) {
const userId = req.params.id;
// Prevent self-deletion
if (userId == req.user.id) {
if (String(userId) === String(req.user.id)) {
return res.status(400).json({ error: 'Cannot delete your own account' });
}