diff --git a/client/src/config/globals.js b/client/src/config/globals.js index ec51baf..dbceaf9 100644 --- a/client/src/config/globals.js +++ b/client/src/config/globals.js @@ -2,7 +2,7 @@ export const globals = { CHAR_POOL: 'abcdefghijklmnopqrstuvwxyz0123456789', USER_SIGNATURE_LENGTH: 25, CLOCK_TICK_INTERVAL_MILLIS: 100, - MAX_CUSTOM_ROLE_NAME_LENGTH: 30, + MAX_CUSTOM_ROLE_NAME_LENGTH: 50, MAX_CUSTOM_ROLE_DESCRIPTION_LENGTH: 500, TOAST_DURATION_DEFAULT: 6, ACCESS_CODE_LENGTH: 4, diff --git a/client/src/model/Game.js b/client/src/model/Game.js index d030581..b54413a 100644 --- a/client/src/model/Game.js +++ b/client/src/model/Game.js @@ -5,6 +5,5 @@ export class Game { this.timerParams = timerParams; this.hasDedicatedModerator = hasDedicatedModerator; this.moderatorName = moderatorName; - this.accessCode = null; } } diff --git a/client/src/modules/game_creation/GameCreationStepManager.js b/client/src/modules/game_creation/GameCreationStepManager.js index 26429b6..aa162ec 100644 --- a/client/src/modules/game_creation/GameCreationStepManager.js +++ b/client/src/modules/game_creation/GameCreationStepManager.js @@ -54,11 +54,13 @@ export class GameCreationStepManager { title: 'Set an optional timer:', forwardHandler: (e) => { if (e.type === 'click' || e.code === 'Enter') { - const hours = parseInt(document.getElementById('game-hours').value); - const minutes = parseInt(document.getElementById('game-minutes').value); - if ((isNaN(hours) && isNaN(minutes)) - || (isNaN(hours) && minutes > 0 && minutes < 60) - || (isNaN(minutes) && hours > 0 && hours < 6) + let hours = parseInt(document.getElementById('game-hours').value); + let minutes = parseInt(document.getElementById('game-minutes').value); + hours = isNaN(hours) ? null : hours; + minutes = isNaN(minutes) ? null : minutes; + if ((hours === null && minutes === null) + || (hours === null && minutes > 0 && minutes < 60) + || (minutes === null && hours > 0 && hours < 6) || (hours === 0 && minutes > 0 && minutes < 60) || (minutes === 0 && hours > 0 && hours < 6) || (hours > 0 && hours < 6 && minutes >= 0 && minutes < 60) @@ -571,7 +573,7 @@ function processNewCustomRoleSubmission (name, description, team, deckManager, i } function hasTimer (hours, minutes) { - return (!isNaN(hours) || !isNaN(minutes)); + return hours !== null || minutes !== null; } function validateName (name) { diff --git a/client/src/modules/game_creation/RoleBox.js b/client/src/modules/game_creation/RoleBox.js index 019e129..dfac0a9 100644 --- a/client/src/modules/game_creation/RoleBox.js +++ b/client/src/modules/game_creation/RoleBox.js @@ -333,7 +333,7 @@ function validateCustomRoleCookie (cookie) { const cookieJSON = JSON.parse(cookie); if (Array.isArray(cookieJSON)) { for (const entry of cookieJSON) { - if (typeof entry === 'object') { + if (entry !== null && typeof entry === 'object') { if (typeof entry.role !== 'string' || entry.role.length > globals.MAX_CUSTOM_ROLE_NAME_LENGTH || typeof entry.team !== 'string' || (entry.team !== globals.ALIGNMENT.GOOD && entry.team !== globals.ALIGNMENT.EVIL) || typeof entry.description !== 'string' || entry.description.length > globals.MAX_CUSTOM_ROLE_DESCRIPTION_LENGTH diff --git a/server/api/GamesAPI.js b/server/api/GamesAPI.js index fbbbaa9..9eaaa8d 100644 --- a/server/api/GamesAPI.js +++ b/server/api/GamesAPI.js @@ -36,7 +36,6 @@ router.patch('/restart', (req, res, next) => { }); router.post('/create', gameEndpointLimiter, function (req, res) { - logger.debug('Received request to create new game: ' + JSON.stringify(req.body, null, 4)); const gameCreationPromise = gameManager.createGame(req.body, false); gameCreationPromise.then((result) => { if (result instanceof Error) { diff --git a/server/config/globals.js b/server/config/globals.js index ca9f0cc..1bdb0d3 100644 --- a/server/config/globals.js +++ b/server/config/globals.js @@ -3,6 +3,12 @@ const globals = { ACCESS_CODE_LENGTH: 4, ACCESS_CODE_GENERATION_ATTEMPTS: 50, CLOCK_TICK_INTERVAL_MILLIS: 100, + MAX_CUSTOM_ROLE_NAME_LENGTH: 50, + MAX_CUSTOM_ROLE_DESCRIPTION_LENGTH: 500, + ALIGNMENT: { + GOOD: 'good', + EVIL: 'evil' + }, CORS: process.env.NODE_ENV?.trim() === 'development' ? { origin: '*', diff --git a/server/modules/GameManager.js b/server/modules/GameManager.js index 7977cd3..9291e44 100644 --- a/server/modules/GameManager.js +++ b/server/modules/GameManager.js @@ -22,11 +22,12 @@ class GameManager { }; createGame = (gameParams) => { - const expectedKeys = ['deck', 'hasTimer', 'timerParams', 'moderatorName']; + const expectedKeys = ['deck', 'hasTimer', 'timerParams', 'moderatorName', 'hasDedicatedModerator']; if (typeof gameParams !== 'object' || expectedKeys.some((key) => !Object.keys(gameParams).includes(key)) + || !valid(gameParams) ) { - this.logger.error('Tried to create game with invalid options: ' + JSON.stringify(gameParams)); + this.logger.error('Tried to create game with invalid options.'); return Promise.reject(globals.ERROR_MESSAGE.BAD_CREATE_REQUEST); } else { this.pruneStaleGames(); @@ -493,6 +494,54 @@ function getGameSize (cards) { return quantity; } +function valid(gameParams) { + return typeof gameParams.hasTimer === "boolean" + && typeof gameParams.hasDedicatedModerator === "boolean" + && typeof gameParams.moderatorName === "string" + && gameParams.moderatorName.length > 0 + && gameParams.moderatorName.length <= 30 + && timerParamsAreValid(gameParams.hasTimer, gameParams.timerParams) + && deckIsValid(gameParams.deck); +} + +function timerParamsAreValid(hasTimer, timerParams) { + if (hasTimer === false) { + return timerParams === null + } else { + if (timerParams === null || typeof timerParams !== 'object') { + return false; + } + + return (timerParams.hours === null && timerParams.minutes > 0 && timerParams.minutes < 60) + || (timerParams.minutes === null && timerParams.hours > 0 && timerParams.hours < 6) + || (timerParams.hours === 0 && timerParams.minutes > 0 && timerParams.minutes < 60) + || (timerParams.minutes === 0 && timerParams.hours > 0 && timerParams.hours < 6) + || (timerParams.hours > 0 && timerParams.hours < 6 && timerParams.minutes >= 0 && timerParams.minutes < 60) + } +} + +function deckIsValid(deck) { + if (Array.isArray(deck) && deck.length > 0) { + for (const entry of deck) { + if (entry !== null && typeof entry === 'object') { + if (typeof entry.role !== 'string' || entry.role.length > globals.MAX_CUSTOM_ROLE_NAME_LENGTH + || typeof entry.team !== 'string' || (entry.team !== globals.ALIGNMENT.GOOD && entry.team !== globals.ALIGNMENT.EVIL) + || typeof entry.description !== 'string' || entry.description.length > globals.MAX_CUSTOM_ROLE_DESCRIPTION_LENGTH + || (entry.custom && typeof entry.custom !== 'boolean') + || typeof entry.quantity !== 'number' || entry.quantity < 1 || entry.quantity > 50 + ) { + return false; + } + } else { + return false; + } + } + return true; + } + + return false; +} + function addSpectator (game, name, logger, namespace) { const spectator = new Person( createRandomId(),