fix: address CodeQL security findings (v0.5.2)
- Rate-limit SPA fallback route (missing rate limiting on fs access) - Add csrfMiddleware to all state-changing auth routes (logout, create user, change password, delete user) — previously bypassed global CSRF middleware due to router registration order - Fix incomplete vCard escaping: escape backslashes before other special characters to prevent injection via contact fields - Restrict CI GITHUB_TOKEN to contents: read (least privilege) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+5
-5
@@ -13,7 +13,7 @@ const session = require('express-session');
|
||||
const rateLimit = require('express-rate-limit');
|
||||
const db = require('./db');
|
||||
|
||||
const { generateToken } = require('./middleware/csrf');
|
||||
const { generateToken, csrfMiddleware } = require('./middleware/csrf');
|
||||
const router = express.Router();
|
||||
|
||||
// --------------------------------------------------------
|
||||
@@ -218,7 +218,7 @@ router.post('/login', loginLimiter, async (req, res) => {
|
||||
* POST /api/v1/auth/logout
|
||||
* Response: { ok: true }
|
||||
*/
|
||||
router.post('/logout', requireAuth, (req, res) => {
|
||||
router.post('/logout', requireAuth, csrfMiddleware, (req, res) => {
|
||||
req.session.destroy((err) => {
|
||||
if (err) {
|
||||
console.error('[Auth] Logout-Fehler:', err);
|
||||
@@ -274,7 +274,7 @@ router.get('/users', requireAuth, requireAdmin, (req, res) => {
|
||||
* Body: { username, display_name, password, avatar_color?, role? }
|
||||
* Response: { user: { id, username, display_name, avatar_color, role } }
|
||||
*/
|
||||
router.post('/users', requireAuth, requireAdmin, async (req, res) => {
|
||||
router.post('/users', requireAuth, requireAdmin, csrfMiddleware, async (req, res) => {
|
||||
try {
|
||||
const { username, display_name, password, avatar_color = '#007AFF', role = 'member' } = req.body;
|
||||
|
||||
@@ -313,7 +313,7 @@ router.post('/users', requireAuth, requireAdmin, async (req, res) => {
|
||||
* Body: { current_password: string, new_password: string }
|
||||
* Response: { ok: true }
|
||||
*/
|
||||
router.patch('/me/password', requireAuth, async (req, res) => {
|
||||
router.patch('/me/password', requireAuth, csrfMiddleware, async (req, res) => {
|
||||
try {
|
||||
const { current_password, new_password } = req.body;
|
||||
|
||||
@@ -345,7 +345,7 @@ router.patch('/me/password', requireAuth, async (req, res) => {
|
||||
* Admin only. Löscht ein Familienmitglied.
|
||||
* Response: { ok: true }
|
||||
*/
|
||||
router.delete('/users/:id', requireAuth, requireAdmin, (req, res) => {
|
||||
router.delete('/users/:id', requireAuth, requireAdmin, csrfMiddleware, (req, res) => {
|
||||
try {
|
||||
const userId = parseInt(req.params.id, 10);
|
||||
|
||||
|
||||
+12
-1
@@ -163,10 +163,21 @@ app.get('/health', (req, res) => {
|
||||
res.json({ status: 'ok', timestamp: new Date().toISOString() });
|
||||
});
|
||||
|
||||
// --------------------------------------------------------
|
||||
// Rate-Limiter für SPA-Fallback (verhindert Dateisystem-Hammering)
|
||||
// --------------------------------------------------------
|
||||
const spaLimiter = rateLimit({
|
||||
windowMs: 60_000,
|
||||
max: 200,
|
||||
standardHeaders: true,
|
||||
legacyHeaders: false,
|
||||
message: { error: 'Zu viele Anfragen. Bitte warte kurz.', code: 429 },
|
||||
});
|
||||
|
||||
// --------------------------------------------------------
|
||||
// SPA Fallback: Alle nicht-API-Routen → index.html
|
||||
// --------------------------------------------------------
|
||||
app.get('*', (req, res) => {
|
||||
app.get('*', spaLimiter, (req, res) => {
|
||||
if (req.path.startsWith('/api/')) {
|
||||
return res.status(404).json({ error: 'Nicht gefunden.', code: 404 });
|
||||
}
|
||||
|
||||
@@ -171,7 +171,7 @@ router.get('/:id/vcard', (req, res) => {
|
||||
const contact = db.get().prepare('SELECT * FROM contacts WHERE id = ?').get(id);
|
||||
if (!contact) return res.status(404).json({ error: 'Kontakt nicht gefunden', code: 404 });
|
||||
|
||||
const esc = (v) => String(v || '').replace(/\n/g, '\\n').replace(/,/g, '\\,').replace(/;/g, '\\;');
|
||||
const esc = (v) => String(v || '').replace(/\\/g, '\\\\').replace(/\n/g, '\\n').replace(/,/g, '\\,').replace(/;/g, '\\;');
|
||||
|
||||
const lines = [
|
||||
'BEGIN:VCARD',
|
||||
|
||||
Reference in New Issue
Block a user