From bbcbc687aea3d4d5ea4b96b9d0f1baa69ed7e929 Mon Sep 17 00:00:00 2001 From: Bruce Berrios <58147810+Bruception@users.noreply.github.com> Date: Sun, 6 Feb 2022 07:33:15 -0500 Subject: [PATCH] Add results schema validation (#2417) by Bruception --- backend/api/controllers/result.js | 21 ++++----- backend/api/routes/result.js | 26 +++++------ backend/api/schemas/result-schema.js | 66 ++++++++++++++++++++++++++++ 3 files changed, 87 insertions(+), 26 deletions(-) create mode 100644 backend/api/schemas/result-schema.js diff --git a/backend/api/controllers/result.js b/backend/api/controllers/result.js index 15ffd9402..05a6b7900 100644 --- a/backend/api/controllers/result.js +++ b/backend/api/controllers/result.js @@ -32,23 +32,27 @@ try { } class ResultController { - static async getResults(req, res) { + static async getResults(req, _res) { const { uid } = req.ctx.decodedToken; - const results = await ResultDAO.getResults(uid); - return res.status(200).json(results); + + return await ResultDAO.getResults(uid); } static async deleteAll(req, res) { const { uid } = req.ctx.decodedToken; + await ResultDAO.deleteAll(uid); Logger.log("user_results_deleted", "", uid); + return res.sendStatus(200); } static async updateTags(req, res) { const { uid } = req.ctx.decodedToken; const { tags, resultid } = req.body; + await ResultDAO.updateTags(uid, resultid, tags); + return res.sendStatus(200); } @@ -58,16 +62,7 @@ class ResultController { result.uid = uid; if (validateObjectValues(result) > 0) return res.status(400).json({ message: "Bad input" }); - if ( - result.wpm <= 0 || - result.wpm > 350 || - result.acc < 75 || - result.acc > 100 || - result.consistency > 100 - ) { - return res.status(400).json({ message: "Bad input" }); - } - if (result.wpm == result.raw && result.acc != 100) { + if (result.wpm === result.raw && result.acc !== 100) { return res.status(400).json({ message: "Bad input" }); } if ( diff --git a/backend/api/routes/result.js b/backend/api/routes/result.js index a3267f1be..676eae746 100644 --- a/backend/api/routes/result.js +++ b/backend/api/routes/result.js @@ -1,3 +1,4 @@ +const joi = require("joi"); const { authenticateRequest } = require("../../middlewares/auth"); const { Router } = require("express"); const ResultController = require("../controllers/result"); @@ -6,6 +7,7 @@ const { asyncHandlerWrapper, requestValidation, } = require("../../middlewares/api-utils"); +const resultSchema = require("../schemas/result-schema"); const router = Router(); @@ -20,6 +22,11 @@ router.post( "/add", RateLimit.resultsAdd, authenticateRequest(), + requestValidation({ + body: { + result: resultSchema, + }, + }), asyncHandlerWrapper(ResultController.addResult) ); @@ -27,6 +34,12 @@ router.post( "/updateTags", RateLimit.resultsTagsUpdate, authenticateRequest(), + requestValidation({ + body: { + tags: joi.array().items(joi.string()).required(), + resultid: joi.string().required(), + }, + }), asyncHandlerWrapper(ResultController.updateTags) ); @@ -37,17 +50,4 @@ router.post( asyncHandlerWrapper(ResultController.deleteAll) ); -router.get( - "/getLeaderboard/:type/:mode/:mode2", - RateLimit.resultsLeaderboardGet, - asyncHandlerWrapper(ResultController.getLeaderboard) -); - -router.post( - "/checkLeaderboardQualification", - RateLimit.resultsLeaderboardQualificationGet, - authenticateRequest(), - asyncHandlerWrapper(ResultController.checkLeaderboardQualification) -); - module.exports = router; diff --git a/backend/api/schemas/result-schema.js b/backend/api/schemas/result-schema.js new file mode 100644 index 000000000..9971f519e --- /dev/null +++ b/backend/api/schemas/result-schema.js @@ -0,0 +1,66 @@ +const joi = require("joi"); + +const RESULT_SCHEMA = joi + .object({ + acc: joi.number().min(75).max(100).required(), + afkDuration: joi.number().required(), + bailedOut: joi.boolean().required(), + blindMode: joi.boolean().required(), + challenge: joi.string(), + charStats: joi.array().items(joi.number()).required(), + chartData: joi + .alternatives() + .try( + joi.object({ + wpm: joi.array().items(joi.number()).required(), + raw: joi.array().items(joi.number()).required(), + err: joi.array().items(joi.number()).required(), + }), + joi.string().valid("toolong") + ) + .required(), + consistency: joi.number().max(100).required(), + customText: joi.object({ + textLen: joi.number().required(), + isWordRandom: joi.boolean().required(), + isTimeRandom: joi.boolean().required(), + word: joi.number().required().allow(null), + time: joi.number().required().allow(null), + }), + difficulty: joi.string().valid("normal", "expert", "master").required(), + funbox: joi.string().required(), + hash: joi.string().required(), + incompleteTestSeconds: joi.number().required(), + keyConsistency: joi.number().required(), + keyDuration: joi + .alternatives() + .try(joi.array().items(joi.number()), joi.string().valid("toolong")) + .required(), + keySpacing: joi + .alternatives() + .try(joi.array().items(joi.number()), joi.string().valid("toolong")) + .required(), + lang: joi.string(), + language: joi.string().required(), + lazyMode: joi.boolean().required(), + mode: joi + .string() + .valid("time", "words", "quote", "zen", "custom") + .required(), + mode2: joi.alternatives().try(joi.number(), joi.string()).required(), + numbers: joi.boolean().required(), + punctuation: joi.boolean().required(), + quoteLength: joi.number().required(), + rawWpm: joi.number().required(), + restartCount: joi.number().required(), + smoothConsistency: joi.number().required(), + tags: joi.array().items(joi.string()).required(), + testDuration: joi.number().required(), + timestamp: joi.date().timestamp().required(), + uid: joi.string().required(), + wpm: joi.number().min(1).max(350).required(), + wpmConsistency: joi.number().required(), + }) + .required(); + +module.exports = RESULT_SCHEMA;