From 380587b7fd65bc1eb71eef51a3aab324f9877650 Mon Sep 17 00:00:00 2001 From: David Mehren Date: Sun, 1 Oct 2023 20:40:15 +0200 Subject: [PATCH] Ensure case-sensitive DB queries on MySQL/MariaDB MySQLs string comparisons are case-insensitive by default. This allows to hide notes by creating a new note with an alias that equals the lower-cased alias of another note. The new note is returned first by MySQL, so the original one is not accessible anymore. This fixes the problem by using an explicit binary comparison in the affected queries. See https://dev.mysql.com/doc/refman/8.0/en/case-sensitivity.html Signed-off-by: David Mehren --- lib/models/note.js | 17 +++++++++++------ lib/utils.js | 4 ++++ 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/lib/models/note.js b/lib/models/note.js index 4e9a453e..628eb254 100644 --- a/lib/models/note.js +++ b/lib/models/note.js @@ -18,6 +18,7 @@ const S = require('string') // core const config = require('../config') const logger = require('../logger') +const utils = require('../utils') // ot const ot = require('../ot') @@ -191,9 +192,11 @@ module.exports = function (sequelize, DataTypes) { parseNoteIdByAlias: function (_callback) { // try to parse note id by alias (e.g. doc) Note.findOne({ - where: { - alias: noteId - } + where: utils.isMySQL(sequelize) + ? sequelize.where(sequelize.fn('BINARY', sequelize.col('alias')), noteId) + : { + alias: noteId + } }).then(function (note) { if (note) { const filePath = path.join(config.docsPath, path.basename(noteId) + '.md') @@ -296,9 +299,11 @@ module.exports = function (sequelize, DataTypes) { try { if (shortId.isValid(noteId)) { Note.findOne({ - where: { - shortid: noteId - } + where: utils.isMySQL(sequelize) + ? sequelize.where(sequelize.fn('BINARY', sequelize.col('shortid')), noteId) + : { + shortid: noteId + } }).then(function (note) { if (!note) return _callback(null, null) return callback(null, note.id) diff --git a/lib/utils.js b/lib/utils.js index 1e6e7654..01b5eb17 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -4,6 +4,10 @@ exports.isSQLite = function isSQLite (sequelize) { return sequelize.options.dialect === 'sqlite' } +exports.isMySQL = function isMySQL (sequelize) { + return ['mysql', 'mariadb'].includes(sequelize.options.dialect) +} + exports.getImageMimeType = function getImageMimeType (imagePath) { const fileExtension = /[^.]+$/.exec(imagePath)