From 7ed76c180ede46ab1dac6b8ad27e9128a272c2c8 Mon Sep 17 00:00:00 2001 From: Lauri Ojansivu Date: Mon, 29 Dec 2025 16:29:01 +0200 Subject: [PATCH] Security Fix 2: Private-only board setting can be bypassed. Thanks to Joshua Rogers of joshua.hu, Twitter MegaManSec ! --- models/boards.js | 33 +++++++++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/models/boards.js b/models/boards.js index 5ccfbd085..c0a760ea6 100644 --- a/models/boards.js +++ b/models/boards.js @@ -9,6 +9,7 @@ import { TYPE_TEMPLATE_CONTAINER, } from '/config/const'; import Users from "./users"; +import TableVisibilityModeSettings from "./tableVisibilityModeSettings"; // const escapeForRegex = require('escape-string-regexp'); @@ -1780,7 +1781,18 @@ Boards.labelColors = () => { if (Meteor.isServer) { Boards.allow({ - insert: Meteor.userId, + insert(userId, doc) { + // Check if user is logged in + if (!userId) return false; + + // If allowPrivateOnly is enabled, only allow private boards + const allowPrivateOnly = TableVisibilityModeSettings.findOne('tableVisibilityMode-allowPrivateOnly')?.booleanValue; + if (allowPrivateOnly && doc.permission === 'public') { + return false; + } + + return true; + }, update: allowIsBoardAdmin, remove: allowIsBoardAdmin, fetch: ['members'], @@ -1830,6 +1842,21 @@ if (Meteor.isServer) { fetch: ['members'], }); + // Deny changing permission to public if allowPrivateOnly is enabled + Boards.deny({ + update(userId, doc, fieldNames, modifier) { + if (!_.contains(fieldNames, 'permission')) return false; + + const allowPrivateOnly = TableVisibilityModeSettings.findOne('tableVisibilityMode-allowPrivateOnly')?.booleanValue; + if (allowPrivateOnly && modifier.$set && modifier.$set.permission === 'public') { + return true; + } + + return false; + }, + fetch: [], + }); + Meteor.methods({ getBackgroundImageURL(boardId) { check(boardId, String); @@ -2274,6 +2301,8 @@ if (Meteor.isServer) { JsonRoutes.add('POST', '/api/boards', function(req, res) { try { Authentication.checkLoggedIn(req.userId); + const allowPrivateOnly = TableVisibilityModeSettings.findOne('tableVisibilityMode-allowPrivateOnly')?.booleanValue; + const permission = allowPrivateOnly ? 'private' : (req.body.permission || 'private'); const id = Boards.insert({ title: req.body.title, members: [ @@ -2286,7 +2315,7 @@ if (Meteor.isServer) { isWorker: req.body.isWorker || false, }, ], - permission: req.body.permission || 'private', + permission, color: req.body.color || 'belize', migrationVersion: 1, // Latest version - no migration needed });