From ee7aa9921b560cd1e86ba77aba6ef3be495640b6 Mon Sep 17 00:00:00 2001 From: John O'Keefe Date: Sun, 6 Sep 2026 10:11:07 -0400 Subject: [PATCH] fix(adminGames): apply Steam URL normalization and scraper guard to create/update Admin create had no URL handling at all (and crashed on missing steamId via .length), and admin update passed req.body straight to the scraper. Reuse normalizeSteamId in both, 400 on unrecognized input, and reject failed scrapes (non-object / missing title+frontImage) before Game.create/findOneAndUpdate so failures report as 'Steam lookup failed' instead of schema validation noise. --- controllers/adminGames.js | 41 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 39 insertions(+), 2 deletions(-) diff --git a/controllers/adminGames.js b/controllers/adminGames.js index 8c04545..ad97a68 100644 --- a/controllers/adminGames.js +++ b/controllers/adminGames.js @@ -2,6 +2,7 @@ import Game from '../models/Game.js' import steamScraper from '../scripts/scraper.js' import asyncHandler from '../middleware/async.js' import ErrorResponse from '../utils/errorResponse.js' +import normalizeSteamId from '../utils/normalizeSteamId.js' const checkForHexRegExp = new RegExp('^[0-9a-fA-F]{24}$') const checkForTwelveRegExp = new RegExp('^[0-9a-fA-F]{12}$') @@ -41,8 +42,17 @@ export const show = asyncHandler(async (req, res, next) => { */ export const create = asyncHandler(async (req, res, next) => { let oldGame - req.body.steamId.length > 0 - ? (oldGame = await Game.findOne({ steamId: req.body.steamId })) + const steamId = normalizeSteamId(req.body.steamId) + if (steamId === null) + return next( + new ErrorResponse( + 'Enter a Steam app ID or a store.steampowered.com URL.', + 400, + ) + ) + req.body.steamId = steamId + steamId.length > 0 + ? (oldGame = await Game.findOne({ steamId })) : (oldGame = await Game.findOne({ title: req.body.title })) if (oldGame) @@ -56,6 +66,14 @@ export const create = asyncHandler(async (req, res, next) => { const data = req.body.scrape === true ? await steamScraper(req.body) : req.body + if ( + req.body.scrape === true && + (!data || typeof data !== 'object' || !data.title || !data.frontImage) + ) + return next( + new ErrorResponse('Steam lookup failed for that ID/URL.', 400) + ) + if (req.body.scrape === false) { if (req.body.shortDesc && isValid(req.body.shortDesc)) req.body.shortDesc = decode(req.body.shortDesc) if (req.body.reviews && isValid(req.body.reviews)) req.body.reviews = decode(req.body.reviews) @@ -96,6 +114,17 @@ export const update = asyncHandler(async (req, res, next) => { req.body.lastModifiedBy = req.user.id + if (req.body.steamId !== undefined) { + const normalizedSteamId = normalizeSteamId(req.body.steamId) + if (normalizedSteamId === null) + return next( + new ErrorResponse( + 'Enter a Steam app ID or a store.steampowered.com URL.', + 400, + ) + ) + req.body.steamId = normalizedSteamId + } if (req.body.shortDesc && isValid(req.body.shortDesc)) req.body.shortDesc = decode(req.body.shortDesc) if (req.body.reviews && isValid(req.body.reviews)) req.body.reviews = decode(req.body.reviews) if (req.body.summary && isValid(req.body.summary)) req.body.summary = decode(req.body.summary) @@ -109,6 +138,14 @@ export const update = asyncHandler(async (req, res, next) => { const data = req.body.scrape === true ? await steamScraper(req.body) : req.body + if ( + req.body.scrape === true && + (!data || typeof data !== 'object' || !data.title || !data.frontImage) + ) + return next( + new ErrorResponse('Steam lookup failed for that ID/URL.', 400) + ) + game = await Game.findOneAndUpdate({ [gameId]: id }, data, { new: true, runValidators: true,