From 8cab2489597eb666ef652a2c5c0ad24109a7ccc6 Mon Sep 17 00:00:00 2001 From: null Date: Thu, 28 May 2026 03:59:35 -0500 Subject: [PATCH] security fixes --- package-lock.json | 64 +++++++++++++------------------------------- package.json | 4 +-- routes/aboutAdmin.js | 2 +- routes/admin.js | 28 ++++++++++++++----- routes/version.js | 4 +-- 5 files changed, 44 insertions(+), 58 deletions(-) diff --git a/package-lock.json b/package-lock.json index fb71bee..ac4b83d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "bill-tracker", - "version": "0.28.1", + "version": "0.28.3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "bill-tracker", - "version": "0.28.1", + "version": "0.28.3", "license": "ISC", "dependencies": { "@radix-ui/react-alert-dialog": "^1.1.2", @@ -32,8 +32,8 @@ "express": "^4.18.2", "express-rate-limit": "^8.4.1", "lucide-react": "^0.456.0", - "node-cron": "^3.0.3", - "nodemailer": "^6.9.14", + "node-cron": "^4.2.1", + "nodemailer": "^8.0.9", "openid-client": "^5.7.1", "react": "^18.3.1", "react-dom": "^18.3.1", @@ -2770,21 +2770,6 @@ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "license": "MIT" }, - "node_modules/body-parser/node_modules/qs": { - "version": "6.15.1", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.1.tgz", - "integrity": "sha512-6YHEFRL9mfgcAvql/XhwTvf5jKcOiiupt2FiJxHkiX1z4j7WL8J/jRHYLluORvc1XxB5rV20KoeK00gVJamspg==", - "license": "BSD-3-Clause", - "dependencies": { - "side-channel": "^1.1.0" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/braces": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", @@ -3571,14 +3556,14 @@ } }, "node_modules/express": { - "version": "4.22.1", - "resolved": "https://registry.npmjs.org/express/-/express-4.22.1.tgz", - "integrity": "sha512-F2X8g9P1X7uCPZMA3MVf9wcTqlyNp7IhH5qPCI0izhaOIYXaW9L535tGA3qmjRzpH+bZczqq7hVKxTR4NWnu+g==", + "version": "4.22.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.22.2.tgz", + "integrity": "sha512-IuL+Elrou2ZvCFHs18/CIzy2Nzvo25nZ1/D2eIZlz7c+QUayAcYoiM2BthCjs+EBHVpjYjcuLDAiCWgeIX3X1Q==", "license": "MIT", "dependencies": { "accepts": "~1.3.8", "array-flatten": "1.1.1", - "body-parser": "~1.20.3", + "body-parser": "~1.20.5", "content-disposition": "~0.5.4", "content-type": "~1.0.4", "cookie": "~0.7.1", @@ -3597,7 +3582,7 @@ "parseurl": "~1.3.3", "path-to-regexp": "~0.1.12", "proxy-addr": "~2.0.7", - "qs": "~6.14.0", + "qs": "~6.15.1", "range-parser": "~1.2.1", "safe-buffer": "5.2.1", "send": "~0.19.0", @@ -5364,13 +5349,10 @@ } }, "node_modules/node-cron": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/node-cron/-/node-cron-3.0.3.tgz", - "integrity": "sha512-dOal67//nohNgYWb+nWmg5dkFdIwDm8EpeGYMekPMrngV3637lqnX0lbUcCtgibHTz6SEz7DAIjKvKDFYCnO1A==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/node-cron/-/node-cron-4.2.1.tgz", + "integrity": "sha512-lgimEHPE/QDgFlywTd8yTR61ptugX3Qer29efeyWw2rv259HtGBNn1vZVmp8lB9uo9wC0t/AT4iGqXxia+CJFg==", "license": "ISC", - "dependencies": { - "uuid": "8.3.2" - }, "engines": { "node": ">=6.0.0" } @@ -5383,9 +5365,9 @@ "license": "MIT" }, "node_modules/nodemailer": { - "version": "6.10.1", - "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.10.1.tgz", - "integrity": "sha512-Z+iLaBGVaSjbIzQ4pX6XV41HrooLsQ10ZWPUehGmuantvzWoDVBnmsdUcOIDM1t+yPor5pDhVlDESgOMEGxhHA==", + "version": "8.0.9", + "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-8.0.9.tgz", + "integrity": "sha512-5ofa7BUN8+C+Hckh5V2GjeeOGRQBx0CJQA6KxrvuZfC8iU4/q7sLn8XrtEEhJkjV6HdyIiQs7Bba6bTao8JhkA==", "license": "MIT-0", "engines": { "node": ">=6.0.0" @@ -5792,9 +5774,9 @@ } }, "node_modules/qs": { - "version": "6.14.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.2.tgz", - "integrity": "sha512-V/yCWTTF7VJ9hIh18Ugr2zhJMP01MY7c5kh4J870L7imm6/DIzBsNLTXzMwUA3yZ5b/KBqLx8Kp3uRvd7xSe3Q==", + "version": "6.15.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.2.tgz", + "integrity": "sha512-Rzq0KEyX/w/tEybncDgdkZrJgVUsUMk3xjh3t5bv3S1HTAtg+uOYt72+ZfwiQwKdysThkTBdL/rTi6HDmX9Ddw==", "license": "BSD-3-Clause", "dependencies": { "side-channel": "^1.1.0" @@ -7107,16 +7089,6 @@ "node": ">= 0.4.0" } }, - "node_modules/uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", - "deprecated": "uuid@10 and below is no longer supported. For ESM codebases, update to uuid@latest. For CommonJS codebases, use uuid@11 (but be aware this version will likely be deprecated in 2028).", - "license": "MIT", - "bin": { - "uuid": "dist/bin/uuid" - } - }, "node_modules/vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", diff --git a/package.json b/package.json index 44db7d2..0674dad 100644 --- a/package.json +++ b/package.json @@ -37,8 +37,8 @@ "express": "^4.18.2", "express-rate-limit": "^8.4.1", "lucide-react": "^0.456.0", - "node-cron": "^3.0.3", - "nodemailer": "^6.9.14", + "node-cron": "^4.2.1", + "nodemailer": "^8.0.9", "openid-client": "^5.7.1", "react": "^18.3.1", "react-dom": "^18.3.1", diff --git a/routes/aboutAdmin.js b/routes/aboutAdmin.js index 1721384..d6711d6 100644 --- a/routes/aboutAdmin.js +++ b/routes/aboutAdmin.js @@ -463,7 +463,7 @@ router.post('/check-updates', requireAuth, requireAdmin, async (req, res) => { const result = await checkForUpdates(true); res.json(result); } catch (err) { - res.status(500).json({ error: err.message || 'Update check failed' }); + res.status(500).json({ error: 'Update check failed' }); } }); diff --git a/routes/admin.js b/routes/admin.js index ca0df83..205e9a2 100644 --- a/routes/admin.js +++ b/routes/admin.js @@ -2,6 +2,7 @@ const express = require('express'); const router = express.Router(); const { getDb, rollbackMigration } = require('../db/database'); const { hashPassword } = require('../services/authService'); +const { logAudit } = require('../services/auditService'); const { createBackup, deleteBackup, @@ -162,10 +163,18 @@ router.post('/users', async (req, res) => { "INSERT INTO users (username, password_hash, role, first_login) VALUES (?, ?, 'user', 1)" ).run(username, hash); - res.status(201).json( - db.prepare('SELECT id, username, role, active, is_default_admin, must_change_password, first_login, created_at FROM users WHERE id = ?') - .get(result.lastInsertRowid) - ); + const created = db.prepare( + 'SELECT id, username, role, active, is_default_admin, must_change_password, first_login, created_at FROM users WHERE id = ?' + ).get(result.lastInsertRowid); + + logAudit({ + user_id: req.user.id, action: 'admin.user.create', + entity_type: 'user', entity_id: created.id, + details: { created_username: username }, + ip_address: req.ip, user_agent: req.get('user-agent'), + }); + + res.status(201).json(created); }); // PUT /api/admin/users/:id/password @@ -182,12 +191,17 @@ router.put('/users/:id/password', async (req, res) => { db.prepare("UPDATE users SET password_hash=?, must_change_password=1, updated_at=datetime('now') WHERE id=?") .run(hash, req.params.id); db.prepare('DELETE FROM sessions WHERE user_id = ?').run(req.params.id); + + logAudit({ + user_id: req.user.id, action: 'admin.password.reset', + entity_type: 'user', entity_id: Number(req.params.id), + details: { target_username: user.username }, + ip_address: req.ip, user_agent: req.get('user-agent'), + }); + res.json({ success: true }); }); -// Import audit service -const { logAudit } = require('../services/auditService'); - // PUT /api/admin/users/:id/role // Promote/demote an existing user. Prevents removing the last admin or // changing your own role mid-session. diff --git a/routes/version.js b/routes/version.js index 82f1bc5..34237ab 100644 --- a/routes/version.js +++ b/routes/version.js @@ -73,7 +73,7 @@ router.get('/history', (req, res) => { updated_at: updatedAt, }); } catch (err) { - res.status(500).json({ error: err.message || 'Failed to read release history' }); + res.status(500).json({ error: 'Failed to read release history' }); } }); @@ -84,7 +84,7 @@ router.get('/update-status', async (req, res) => { try { res.json(await checkForUpdates(false)); } catch (err) { - res.json({ current_version: pkg.version, latest_version: null, up_to_date: null, has_update: false, error: err.message }); + res.json({ current_version: pkg.version, latest_version: null, up_to_date: null, has_update: false, error: 'Update check unavailable' }); } });