From 2386919998ebc92b5ebb415b3a3a673ba15644b0 Mon Sep 17 00:00:00 2001 From: "Claude (Chronicler #51)" Date: Wed, 1 Apr 2026 05:27:40 +0000 Subject: [PATCH] fix: Implement CSRF protection for Trinity Console MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CRITICAL SECURITY FIX - Prevents Cross-Site Request Forgery attacks Changes: - Installed csurf middleware (session-based tokens) - Added CSRF middleware to all /admin routes in src/index.js - Configured admin router to pass csrfToken to all views - Updated layout.ejs to send CSRF token with htmx requests - Added EJS view engine configuration - Added body parsing middleware (json + urlencoded) Security Impact: - Prevents malicious sites from executing admin actions using cookies - All POST requests now require valid CSRF token - Invalid tokens return 403 Forbidden - Session-based tokens (no cookies needed) Protected Routes: - /admin/servers/:id/sync (force whitelist sync) - /admin/servers/:id/toggle-whitelist (whitelist toggle) - /admin/grace/:id/extend (grace period extension) - /admin/grace/:id/manual (manual payment override) - /admin/roles/resync/:id (role assignment) Attack Scenario Prevented: User visits malicious site while logged into Trinity Console → Site tries to submit form to admin endpoint → Request includes session cookie but NO CSRF token → Server rejects with 403 Forbidden → Attack failed! Note: csurf is deprecated but still functional. For future refactor, consider csrf-csrf or Express 5 built-in protection. Refs: TRINITY-CONSOLE-PRE-LAUNCH-CHECKLIST.md - Fix #1 Chronicler: #51 Signed-off-by: Claude (Chronicler #51) --- services/arbiter-3.0/package.json | 1 + services/arbiter-3.0/src/index.js | 12 +++++++++++- services/arbiter-3.0/src/routes/admin/index.js | 6 ++++++ services/arbiter-3.0/src/views/layout.ejs | 6 ++++++ 4 files changed, 24 insertions(+), 1 deletion(-) diff --git a/services/arbiter-3.0/package.json b/services/arbiter-3.0/package.json index e809094..22cc786 100644 --- a/services/arbiter-3.0/package.json +++ b/services/arbiter-3.0/package.json @@ -10,6 +10,7 @@ "dependencies": { "body-parser": "^1.20.2", "cookie-parser": "^1.4.7", + "csurf": "^1.11.0", "discord.js": "^14.14.1", "dotenv": "^16.4.5", "ejs": "^3.1.9", diff --git a/services/arbiter-3.0/src/index.js b/services/arbiter-3.0/src/index.js index ed7b837..2c4e4c1 100644 --- a/services/arbiter-3.0/src/index.js +++ b/services/arbiter-3.0/src/index.js @@ -4,6 +4,7 @@ const session = require('express-session'); const passport = require('passport'); const DiscordStrategy = require('passport-discord').Strategy; const { Client, GatewayIntentBits, REST, Routes } = require('discord.js'); +const csrf = require('csurf'); const authRoutes = require('./routes/auth'); const adminRoutes = require('./routes/admin'); @@ -32,6 +33,12 @@ passport.use(new DiscordStrategy({ // Initialize Express App const app = express(); app.set('trust proxy', 1); +app.set('view engine', 'ejs'); +app.set('views', __dirname + '/views'); + +// Body parsing middleware +app.use(express.json()); +app.use(express.urlencoded({ extended: true })); // Make Discord client accessible to routes app.locals.client = client; @@ -57,9 +64,12 @@ app.get('/health', (req, res) => { }); }); +// CSRF Protection (session-based) +const csrfProtection = csrf({ cookie: false }); + // Register Routes app.use('/auth', authRoutes); -app.use('/admin', adminRoutes); +app.use('/admin', csrfProtection, adminRoutes); app.use('/webhook', webhookRoutes); // Start Application diff --git a/services/arbiter-3.0/src/routes/admin/index.js b/services/arbiter-3.0/src/routes/admin/index.js index 6b682f9..7dd4df6 100644 --- a/services/arbiter-3.0/src/routes/admin/index.js +++ b/services/arbiter-3.0/src/routes/admin/index.js @@ -12,6 +12,12 @@ const rolesRouter = require('./roles'); router.use(requireTrinityAccess); +// Make CSRF token available to all admin views +router.use((req, res, next) => { + res.locals.csrfToken = req.csrfToken(); + next(); +}); + router.get('/', (req, res) => { res.redirect('/admin/dashboard'); }); diff --git a/services/arbiter-3.0/src/views/layout.ejs b/services/arbiter-3.0/src/views/layout.ejs index 69899d3..90f56d2 100644 --- a/services/arbiter-3.0/src/views/layout.ejs +++ b/services/arbiter-3.0/src/views/layout.ejs @@ -6,6 +6,12 @@ <%= title %> | Trinity Console +