From 779e2d61f4d76a6ac3769eb32cb2f14a8da4f149 Mon Sep 17 00:00:00 2001 From: GotPPay Date: Mon, 29 Jan 2018 23:23:36 +0100 Subject: [PATCH] backend user input handling --- backend/config/constants.js | 70 +++++++++++++-------- backend/controllers/skill.js | 15 +++++ backend/helpers/skillValidator.js | 100 ++++++++++++++++++++++++++++++ 3 files changed, 159 insertions(+), 26 deletions(-) create mode 100644 backend/helpers/skillValidator.js diff --git a/backend/config/constants.js b/backend/config/constants.js index 093f427..ef7c57f 100644 --- a/backend/config/constants.js +++ b/backend/config/constants.js @@ -1,43 +1,61 @@ const constants = {}; constants.amazonResultCodes = { - OK:200, - ACCEPTED:202, - BAD_REQUEST:400, - UNAUTHORIZED:401, - NOT_FOUND:404, - CONFLICT:409, - PAYLOAD_TOO_LARGE:413 -} + OK: 200, + ACCEPTED: 202, + BAD_REQUEST: 400, + UNAUTHORIZED: 401, + NOT_FOUND: 404, + CONFLICT: 409, + PAYLOAD_TOO_LARGE: 413, +}; constants.apiResultCodes = { - GENERIC_ERROR : -1, - OK:0, - AMAZON_ERROR:1, //amazon api works, but error is some of the amazonResultCodes - AMAZON_FAIL:2, //amazon api doesn't work - DATABASE_ERROR:3, - NO_SKILL:4, - INCONSISTENT_STATE:5, -} + GENERIC_ERROR: -1, + OK: 0, + AMAZON_ERROR: 1, //amazon api works, but error is some of the amazonResultCodes + AMAZON_FAIL: 2, //amazon api doesn't work + DATABASE_ERROR: 3, + NO_SKILL: 4, + INCONSISTENT_STATE: 5, + INVALID_SKILL: 6, +}; constants.HTTPResultCodes = { - INTERNAL_SERVER_ERROR : 500, -} + INTERNAL_SERVER_ERROR: 500, +}; constants.SKILL_ID_LENGTH = 24; constants.voiceResponseStrings = { - QUESTION_NOT_FOUND : 'Sorry, I didnt understand', - GENERIC_CONTINUE : 'Say something to continue', - DIDNT_ASK_ANYTHING : 'There was no question to answer to', -} + QUESTION_NOT_FOUND: 'Sorry, I didnt understand', + GENERIC_CONTINUE: 'Say something to continue', + DIDNT_ASK_ANYTHING: 'There was no question to answer to', +}; //Timing is given in [ms] constants.voiceResponseTimings = { - PAUSE_BETWEEN_QUESTIONS : 650, - PAUSE_AFTER_WELCOME_MESSAGE : 650, -} + PAUSE_BETWEEN_QUESTIONS: 650, + PAUSE_AFTER_WELCOME_MESSAGE: 650, +}; +constants.stringConstraints = { + INTENT_EXPLANATION_MAX_LENGTH: 70, + INTENT_NAME_MAX_LENGTH: 30, + INTENT_NAME_MIN_LENGTH: 2, -module.exports = constants; \ No newline at end of file + QUESTION_MAX_LENGTH: 150, + QUESTION_MIN_LENGTH: 2, + + ANSWER_MAX_LENGTH: 150, + ANSWER_MIN_LENGTH: 2, + + INVOCATION_NAME_MAX_LENGTH: 50, + INVOCATION_NAME_MIN_LENGTH: 2, + INVOCATION_ANSWER_MAX_LENGTH: 100, + + EMAIL_MAX_LENGTH: 100, +}; + +module.exports = constants; diff --git a/backend/controllers/skill.js b/backend/controllers/skill.js index a0c2f5f..6ee4906 100644 --- a/backend/controllers/skill.js +++ b/backend/controllers/skill.js @@ -2,6 +2,7 @@ var express = require ('express'), router = express.Router (); const constants = require ('../config/constants'); var databaseHelper = require ('../helpers/database'); var amazonHelper = require ('../helpers/amazon'); +var skillValidator = require('../helpers/skillValidator'); var bodyParser = require ('body-parser'); var alexa = require ('../models/alexa'); @@ -31,6 +32,20 @@ router.put ('/:id', bodyParser.json (), async (req, res, next) => { delete skill.updateOnAmazon; delete skill._id; + //Validate skill + if (!skillValidator.validateSkill(skill)){ + //skill not valid + res + .status ( + constants.HTTPResultCodes.INTERNAL_SERVER_ERROR + ) + .json ({ + result: constants.apiResultCodes.INVALID_SKILL, + message: '', + }); + return; + } + //First get current skill from DB databaseHelper .getSkill (id) diff --git a/backend/helpers/skillValidator.js b/backend/helpers/skillValidator.js new file mode 100644 index 0000000..6d78cdf --- /dev/null +++ b/backend/helpers/skillValidator.js @@ -0,0 +1,100 @@ +const constants = require ('../config/constants'); + +validateEmail = function (email) { + if (email.length > constants.stringConstraints.EMAIL_MAX_LENGTH) return false; + let validEmailRegex = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/; + return validEmailRegex.test (email); +}; + +validateIntentName = function (intentName) { + if ( + intentName.length < constants.stringConstraints.INTENT_NAME_MIN_LENGTH || + intentName.length > constants.stringConstraints.INTENT_NAME_MAX_LENGTH + ) + return false; + let validIntentNameRegex = /^[a-z]*$/i; + return validIntentNameRegex.test (intentName); +}; + +validateQuestion = function (question) { + if ( + question.length < constants.stringConstraints.QUESTION_MIN_LENGTH || + question.length > constants.stringConstraints.QUESTION_MAX_LENGTH + ) + return false; + let validQuestionNameRegex = /^[a-z,.' ]*$/i; + return validQuestionNameRegex.test (question); +}; + +validateAnswer = function (answer) { + if ( + answer.length < constants.stringConstraints.ANSWER_MIN_LENGTH || + answer.length > constants.stringConstraints.ANSWER_MAX_LENGTH + ) + return false; + let validAnswerRegex = /^[a-z,.' ]*$/i; + return validAnswerRegex.test (answer); +}; + +validateInvocationName = function (invocationName) { + if ( + invocationName.length < constants.stringConstraints.INVOCATION_NAME_MIN_LENGTH || + invocationName.length > constants.stringConstraints.INVOCATION_NAME_MAX_LENGTH + ) + return false; + let validInvocationNameRegex = /^[a-z,.' ]*$/i; + return validInvocationNameRegex.test (invocationName); +}; + +validateInvocationAnswer = function (invocationAnswer) { + if (invocationAnswer.length > constants.stringConstraints.INVOCATION_ANSWER_MAX_LENGTH) + return false; + let validInvocationAnswerRegex = /^[a-z,.' ]*$/i; + return validInvocationAnswerRegex.test (invocationAnswer); +}; + +validateIntentExplanation = function (explanation) { + if (explanation.length > constants.stringConstraints.INTENT_EXPLANATION_MAX_LENGTH) + return false; + let validExplanationRegex = /^[a-z,.' ]*$/i; + return validExplanationRegex.test (explanation); +}; + +module.exports = { + validateSkill: function (skill) { + try { + if ( + !validateEmail (skill.contactEmail) || + !validateInvocationName (skill.invocationName) || + !validateInvocationAnswer (skill.invocationAnswer) + ) + return false; + + for (let i = 0; i < skill.intents.length; i++) { + if (!validateIntentName (skill.intents[i].intentName)) return false; + if (!validateAnswer (skill.intents[i].answer)) return false; + + for (let j = 0; j < skill.intents.length; j++) { + if (i === j) continue; + if (skill.intents[i].intentName === skill.intents[j].intentName) + return false; + } + + for (let j = 0; j < skill.intents[i].questions.length; j++) { + if (!validateQuestion (skill.intents[i].questions[j])) return false; + + for (let k = 0; k < skill.intents[i].questions.length; k++) { + if (j === k) continue; + if (skill.intents[i].questions[j] === skill.intents[i].questions[k]) + return false; + } + } + } + + return true; + } catch (e) { + console.log ('Error : ' + e); + return false; + } + }, +};