From 9052ceedd3659aca1e6b227e62fe0687c1d94336 Mon Sep 17 00:00:00 2001 From: advplyr Date: Sat, 31 May 2025 17:01:58 -0500 Subject: [PATCH] Sanitize media item & episode description on update --- server/controllers/PodcastController.js | 10 ++++++++++ server/models/Book.js | 11 ++++++++++- server/models/Podcast.js | 10 ++++++++++ 3 files changed, 30 insertions(+), 1 deletion(-) diff --git a/server/controllers/PodcastController.js b/server/controllers/PodcastController.js index 6395e05b..1ebe1d11 100644 --- a/server/controllers/PodcastController.js +++ b/server/controllers/PodcastController.js @@ -9,6 +9,7 @@ const fs = require('../libs/fsExtra') const { getPodcastFeed, findMatchingEpisodes } = require('../utils/podcastUtils') const { getFileTimestampsWithIno, filePathToPOSIX } = require('../utils/fileUtils') const { validateUrl } = require('../utils/index') +const htmlSanitizer = require('../utils/htmlSanitizer') const Scanner = require('../scanner/Scanner') const CoverManager = require('../managers/CoverManager') @@ -404,6 +405,15 @@ class PodcastController { const supportedStringKeys = ['title', 'subtitle', 'description', 'pubDate', 'episode', 'season', 'episodeType'] for (const key in req.body) { if (supportedStringKeys.includes(key) && typeof req.body[key] === 'string') { + // Sanitize description HTML + if (key === 'description' && req.body[key]) { + const sanitizedDescription = htmlSanitizer.sanitize(req.body[key]) + if (sanitizedDescription !== req.body[key]) { + Logger.debug(`[PodcastController] Sanitized description from "${req.body[key]}" to "${sanitizedDescription}"`) + req.body[key] = sanitizedDescription + } + } + updatePayload[key] = req.body[key] } else if (key === 'chapters' && Array.isArray(req.body[key]) && req.body[key].every((ch) => typeof ch === 'object' && ch.title && ch.start)) { updatePayload[key] = req.body[key] diff --git a/server/models/Book.js b/server/models/Book.js index 0dd0b785..96371f3a 100644 --- a/server/models/Book.js +++ b/server/models/Book.js @@ -377,8 +377,17 @@ class Book extends Model { if (typeof payload.metadata[key] == 'number') { payload.metadata[key] = String(payload.metadata[key]) } - + if ((typeof payload.metadata[key] === 'string' || payload.metadata[key] === null) && this[key] !== payload.metadata[key]) { + // Sanitize description HTML + if (key === 'description' && payload.metadata[key]) { + const sanitizedDescription = htmlSanitizer.sanitize(payload.metadata[key]) + if (sanitizedDescription !== payload.metadata[key]) { + Logger.debug(`[Book] "${this.title}" Sanitized description from "${payload.metadata[key]}" to "${sanitizedDescription}"`) + payload.metadata[key] = sanitizedDescription + } + } + this[key] = payload.metadata[key] || null if (key === 'title') { diff --git a/server/models/Podcast.js b/server/models/Podcast.js index fa27821d..d99a66df 100644 --- a/server/models/Podcast.js +++ b/server/models/Podcast.js @@ -2,6 +2,7 @@ const { DataTypes, Model } = require('sequelize') const { getTitlePrefixAtEnd, getTitleIgnorePrefix } = require('../utils') const Logger = require('../Logger') const libraryItemsPodcastFilters = require('../utils/queries/libraryItemsPodcastFilters') +const htmlSanitizer = require('../utils/htmlSanitizer') /** * @typedef PodcastExpandedProperties @@ -215,6 +216,15 @@ class Podcast extends Model { newKey = 'itunesPageURL' } if ((typeof payload.metadata[key] === 'string' || payload.metadata[key] === null) && payload.metadata[key] !== this[newKey]) { + // Sanitize description HTML + if (key === 'description' && payload.metadata[key]) { + const sanitizedDescription = htmlSanitizer.sanitize(payload.metadata[key]) + if (sanitizedDescription !== payload.metadata[key]) { + Logger.debug(`[Podcast] "${this.title}" Sanitized description from "${payload.metadata[key]}" to "${sanitizedDescription}"`) + payload.metadata[key] = sanitizedDescription + } + } + this[newKey] = payload.metadata[key] || null if (key === 'title') {