diff --git a/package-lock.json b/package-lock.json index 7d86518d..e2b632f0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -21,7 +21,6 @@ "express-rate-limit": "^6.5.2", "express-validator": "^6.14.2", "helmet": "^7.0.0", - "html-entities": "^2.3.3", "jsonschema": "^1.4.0", "JSONStream": "^1.3.5", "kleur": "^4.1.4", @@ -4504,11 +4503,6 @@ "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", "dev": true }, - "node_modules/html-entities": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.3.3.tgz", - "integrity": "sha512-DV5Ln36z34NNTDgnz0EWGBLZENelNAtkiFA4kyNOG2tDI6Mz1uSWiq1wAKdyjnJwyDiDO7Fa2SO1CTxPXL8VxA==" - }, "node_modules/html-escaper": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", @@ -13736,11 +13730,6 @@ "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", "dev": true }, - "html-entities": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.3.3.tgz", - "integrity": "sha512-DV5Ln36z34NNTDgnz0EWGBLZENelNAtkiFA4kyNOG2tDI6Mz1uSWiq1wAKdyjnJwyDiDO7Fa2SO1CTxPXL8VxA==" - }, "html-escaper": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", diff --git a/package.json b/package.json index 03c65c26..e85f8f2b 100644 --- a/package.json +++ b/package.json @@ -39,7 +39,6 @@ "express-rate-limit": "^6.5.2", "express-validator": "^6.14.2", "helmet": "^7.0.0", - "html-entities": "^2.3.3", "jsonschema": "^1.4.0", "JSONStream": "^1.3.5", "kleur": "^4.1.4", @@ -101,4 +100,4 @@ "test:coverage-html": "NODE_ENV=test nyc --reporter=html mocha src/* --recursive --exit || true", "test:scripts": "NODE_ENV=development node-dev src/scripts/templateScript.js" } -} \ No newline at end of file +} diff --git a/src/controller/cve-id.controller/index.js b/src/controller/cve-id.controller/index.js index 737a383d..0ab114d0 100644 --- a/src/controller/cve-id.controller/index.js +++ b/src/controller/cve-id.controller/index.js @@ -86,13 +86,14 @@ router.get('/cve-id', */ mw.validateUser, query().custom((query) => { return mw.validateQueryParameterNames(query, ['page', 'state', 'cve_id_year', 'time_reserved.lt', 'time_reserved.gt', 'time_modified.lt', 'time_modified.gt']) }), + query(['page', 'state', 'cve_id_year', 'time_reserved.lt', 'time_reserved.gt', 'time_modified.lt', 'time_modified.gt']).custom((val) => { return mw.containsNoInvalidCharacters(val) }), query(['page']).optional().isInt({ min: CONSTANTS.PAGINATOR_PAGE }), - query(['state']).optional().isString().trim().escape().customSanitizer(val => { return val.toUpperCase() }).isIn(CHOICES).withMessage(errorMsgs.ID_STATES), + query(['state']).optional().isString().trim().customSanitizer(val => { return val.toUpperCase() }).isIn(CHOICES).withMessage(errorMsgs.ID_STATES), query(['cve_id_year']).optional().isNumeric().matches(/^[0-9]{4}$/), - query(['time_reserved.lt']).optional().isString().trim().escape().customSanitizer(val => { return toDate(val) }).not().isEmpty().withMessage(errorMsgs.TIMESTAMP_FORMAT), - query(['time_reserved.gt']).optional().isString().trim().escape().customSanitizer(val => { return toDate(val) }).not().isEmpty().withMessage(errorMsgs.TIMESTAMP_FORMAT), - query(['time_modified.lt']).optional().isString().trim().escape().customSanitizer(val => { return toDate(val) }).not().isEmpty().withMessage(errorMsgs.TIMESTAMP_FORMAT), - query(['time_modified.gt']).optional().isString().trim().escape().customSanitizer(val => { return toDate(val) }).not().isEmpty().withMessage(errorMsgs.TIMESTAMP_FORMAT), + query(['time_reserved.lt']).optional().isString().trim().customSanitizer(val => { return toDate(val) }).not().isEmpty().withMessage(errorMsgs.TIMESTAMP_FORMAT), + query(['time_reserved.gt']).optional().isString().trim().customSanitizer(val => { return toDate(val) }).not().isEmpty().withMessage(errorMsgs.TIMESTAMP_FORMAT), + query(['time_modified.lt']).optional().isString().trim().customSanitizer(val => { return toDate(val) }).not().isEmpty().withMessage(errorMsgs.TIMESTAMP_FORMAT), + query(['time_modified.gt']).optional().isString().trim().customSanitizer(val => { return toDate(val) }).not().isEmpty().withMessage(errorMsgs.TIMESTAMP_FORMAT), parseError, parseGetParams, controller.CVEID_GET_FILTER) @@ -177,9 +178,10 @@ router.post('/cve-id', mw.validateUser, mw.onlyCnas, query().custom((query) => { return mw.validateQueryParameterNames(query, ['amount', 'batch_type', 'short_name', 'cve_year']) }), + query(['amount', 'batch_type', 'short_name', 'cve_year']).custom((val) => { return mw.containsNoInvalidCharacters(val) }), query(['amount']).isInt(), - query(['batch_type']).optional().isString().trim().escape().customSanitizer(val => { return val.toLowerCase() }), - query(['short_name']).isString().trim().escape().isLength({ min: CONSTANTS.MIN_SHORTNAME_LENGTH, max: CONSTANTS.MAX_SHORTNAME_LENGTH }), + query(['batch_type']).optional().isString().trim().customSanitizer(val => { return val.toLowerCase() }), + query(['short_name']).isString().trim().isLength({ min: CONSTANTS.MIN_SHORTNAME_LENGTH, max: CONSTANTS.MAX_SHORTNAME_LENGTH }), query(['cve_year']).isNumeric().matches(/^[0-9]{4}$/), parseError, parsePostParams, @@ -340,8 +342,9 @@ router.put('/cve-id/:id', mw.onlyCnas, param(['id']).isString().matches(CONSTANTS.CVE_ID_REGEX), query().custom((query) => { return mw.validateQueryParameterNames(query, ['state', 'org']) }), - query(['state']).optional().isString().trim().escape().customSanitizer(val => { return val.toUpperCase() }).isIn(MODIFYTARGETS).withMessage(errorMsgs.ID_MODIFY_STATES), - query(['org']).optional().isString().trim().escape(), + query(['state', 'org']).custom((val) => { return mw.containsNoInvalidCharacters(val) }), + query(['state']).optional().isString().trim().customSanitizer(val => { return val.toUpperCase() }).isIn(MODIFYTARGETS).withMessage(errorMsgs.ID_MODIFY_STATES), + query(['org']).optional().isString().trim(), parseError, parsePostParams, mw.cnaMustOwnID, diff --git a/src/controller/cve.controller/index.js b/src/controller/cve.controller/index.js index 2e89fe49..eba11d17 100644 --- a/src/controller/cve.controller/index.js +++ b/src/controller/cve.controller/index.js @@ -157,15 +157,16 @@ router.get('/cve', mw.validateUser, mw.onlySecretariatOrBulkDownload, query().custom((query) => { return mw.validateQueryParameterNames(query, ['page', 'time_modified.lt', 'time_modified.gt', 'state', 'count_only', 'assigner_short_name', 'assigner', 'cna_modified', 'adp_short_name']) }), + query(['page', 'time_modified.lt', 'time_modified.gt', 'state', 'count_only', 'assigner_short_name', 'assigner', 'cna_modified', 'adp_short_name']).custom((val) => { return mw.containsNoInvalidCharacters(val) }), query(['page']).optional().isInt({ min: CONSTANTS.PAGINATOR_PAGE }), - query(['time_modified.lt']).optional().isString().trim().escape().customSanitizer(val => { return toDate(val) }).not().isEmpty().withMessage(errorMsgs.TIMESTAMP_FORMAT), - query(['time_modified.gt']).optional().isString().trim().escape().customSanitizer(val => { return toDate(val) }).not().isEmpty().withMessage(errorMsgs.TIMESTAMP_FORMAT), - query(['state']).optional().isString().trim().escape().customSanitizer(val => { return val.toUpperCase() }).isIn(CHOICES).withMessage(errorMsgs.CVE_FILTERED_STATES), + query(['time_modified.lt']).optional().isString().trim().customSanitizer(val => { return toDate(val) }).not().isEmpty().withMessage(errorMsgs.TIMESTAMP_FORMAT), + query(['time_modified.gt']).optional().isString().trim().customSanitizer(val => { return toDate(val) }).not().isEmpty().withMessage(errorMsgs.TIMESTAMP_FORMAT), + query(['state']).optional().isString().trim().customSanitizer(val => { return val.toUpperCase() }).isIn(CHOICES).withMessage(errorMsgs.CVE_FILTERED_STATES), query(['count_only']).optional().isBoolean({ loose: true }).withMessage(errorMsgs.COUNT_ONLY), - query(['assigner_short_name']).optional().isString().trim().escape().notEmpty().isLength({ min: CONSTANTS.MIN_SHORTNAME_LENGTH, max: CONSTANTS.MAX_SHORTNAME_LENGTH }), - query(['assigner']).optional().isString().trim().escape().notEmpty(), + query(['assigner_short_name']).optional().isString().trim().notEmpty().isLength({ min: CONSTANTS.MIN_SHORTNAME_LENGTH, max: CONSTANTS.MAX_SHORTNAME_LENGTH }), + query(['assigner']).optional().isString().trim().notEmpty(), query(['cna_modified']).optional().isBoolean({ loose: true }).withMessage(errorMsgs.CNA_MODIFIED), - query(['adp_short_name']).optional().isString().trim().escape().notEmpty().isLength({ min: CONSTANTS.MIN_SHORTNAME_LENGTH, max: CONSTANTS.MAX_SHORTNAME_LENGTH }), + query(['adp_short_name']).optional().isString().trim().notEmpty().isLength({ min: CONSTANTS.MIN_SHORTNAME_LENGTH, max: CONSTANTS.MAX_SHORTNAME_LENGTH }), parseError, parseGetParams, controller.CVE_GET_FILTERED) @@ -244,15 +245,16 @@ router.get('/cve_cursor', mw.validateUser, mw.onlySecretariatOrBulkDownload, query().custom((query) => { return mw.validateQueryParameterNames(query, ['time_modified.lt', 'time_modified.gt', 'state', 'count_only', 'assigner_short_name', 'assigner', 'cna_modified', 'adp_short_name', 'next_page', 'previous_page', 'limit']) }), - query(['time_modified.lt']).optional().isString().trim().escape().customSanitizer(val => { return toDate(val) }).not().isEmpty().withMessage(errorMsgs.TIMESTAMP_FORMAT), - query(['time_modified.gt']).optional().isString().trim().escape().customSanitizer(val => { return toDate(val) }).not().isEmpty().withMessage(errorMsgs.TIMESTAMP_FORMAT), - query(['state']).optional().isString().trim().escape().customSanitizer(val => { return val.toUpperCase() }).isIn(CHOICES).withMessage(errorMsgs.CVE_FILTERED_STATES), + query(['time_modified.lt', 'time_modified.gt', 'state', 'count_only', 'assigner_short_name', 'assigner', 'cna_modified', 'adp_short_name', 'next_page', 'previous_page', 'limit']).custom((val) => { return mw.containsNoInvalidCharacters(val) }), + query(['time_modified.lt']).optional().isString().trim().customSanitizer(val => { return toDate(val) }).not().isEmpty().withMessage(errorMsgs.TIMESTAMP_FORMAT), + query(['time_modified.gt']).optional().isString().trim().customSanitizer(val => { return toDate(val) }).not().isEmpty().withMessage(errorMsgs.TIMESTAMP_FORMAT), + query(['state']).optional().isString().trim().customSanitizer(val => { return val.toUpperCase() }).isIn(CHOICES).withMessage(errorMsgs.CVE_FILTERED_STATES), query(['count_only']).optional().isBoolean({ loose: true }).withMessage(errorMsgs.COUNT_ONLY), - query(['assigner_short_name']).optional().isString().trim().escape().notEmpty().isLength({ min: CONSTANTS.MIN_SHORTNAME_LENGTH, max: CONSTANTS.MAX_SHORTNAME_LENGTH }), - query(['assigner']).optional().isString().trim().escape().notEmpty(), + query(['assigner_short_name']).optional().isString().trim().notEmpty().isLength({ min: CONSTANTS.MIN_SHORTNAME_LENGTH, max: CONSTANTS.MAX_SHORTNAME_LENGTH }), + query(['assigner']).optional().isString().trim().notEmpty(), query(['cna_modified']).optional().isBoolean({ loose: true }).withMessage(errorMsgs.CNA_MODIFIED), - query(['adp_short_name']).optional().isString().trim().escape().notEmpty().isLength({ min: CONSTANTS.MIN_SHORTNAME_LENGTH, max: CONSTANTS.MAX_SHORTNAME_LENGTH }), - query(['limit']).optional().isString().trim().escape().notEmpty().isLength({ min: 1, max: CONSTANTS.PAGINATOR_OPTIONS.limit }), + query(['adp_short_name']).optional().isString().trim().notEmpty().isLength({ min: CONSTANTS.MIN_SHORTNAME_LENGTH, max: CONSTANTS.MAX_SHORTNAME_LENGTH }), + query(['limit']).optional().isString().trim().notEmpty().isLength({ min: 1, max: CONSTANTS.PAGINATOR_OPTIONS.limit }), parseError, parseGetParams, controller.CVE_GET_FILTERED_CURSOR) diff --git a/src/controller/org.controller/index.js b/src/controller/org.controller/index.js index b1630aa9..1c94e1b5 100644 --- a/src/controller/org.controller/index.js +++ b/src/controller/org.controller/index.js @@ -77,6 +77,7 @@ router.get('/org', mw.validateUser, mw.onlySecretariat, query().custom((query) => { return mw.validateQueryParameterNames(query, ['page']) }), + query(['page']).custom((val) => { return mw.containsNoInvalidCharacters(val) }), query(['page']).optional().isInt({ min: CONSTANTS.PAGINATOR_PAGE }), parseError, parseGetParams, @@ -156,8 +157,8 @@ router.post('/org', */ mw.validateUser, mw.onlySecretariat, - body(['short_name']).isString().trim().escape().notEmpty().isLength({ min: CONSTANTS.MIN_SHORTNAME_LENGTH, max: CONSTANTS.MAX_SHORTNAME_LENGTH }), - body(['name']).isString().trim().escape().notEmpty(), + body(['short_name']).isString().trim().notEmpty().isLength({ min: CONSTANTS.MIN_SHORTNAME_LENGTH, max: CONSTANTS.MAX_SHORTNAME_LENGTH }), + body(['name']).isString().trim().notEmpty(), body(['authority.active_roles']).optional() .custom(isFlatStringArray) .customSanitizer(toUpperCaseArray) @@ -233,7 +234,7 @@ router.get('/org/:identifier', } */ mw.validateUser, - param(['identifier']).isString().trim().escape(), + param(['identifier']).isString().trim(), parseError, parseGetParams, controller.ORG_SINGLE) @@ -310,10 +311,11 @@ router.put('/org/:shortname', mw.validateUser, mw.onlySecretariat, query().custom((query) => { return mw.validateQueryParameterNames(query, ['new_short_name', 'id_quota', 'name', 'active_roles.add', 'active_roles.remove']) }), - param(['shortname']).isString().trim().escape().isLength({ min: CONSTANTS.MIN_SHORTNAME_LENGTH, max: CONSTANTS.MAX_SHORTNAME_LENGTH }), - query(['new_short_name']).optional().isString().trim().escape().notEmpty().isLength({ min: CONSTANTS.MIN_SHORTNAME_LENGTH, max: CONSTANTS.MAX_SHORTNAME_LENGTH }), + query(['new_short_name', 'id_quota', 'name', 'active_roles.add', 'active_roles.remove']).custom((val) => { return mw.containsNoInvalidCharacters(val) }), + param(['shortname']).isString().trim().isLength({ min: CONSTANTS.MIN_SHORTNAME_LENGTH, max: CONSTANTS.MAX_SHORTNAME_LENGTH }), + query(['new_short_name']).optional().isString().trim().notEmpty().isLength({ min: CONSTANTS.MIN_SHORTNAME_LENGTH, max: CONSTANTS.MAX_SHORTNAME_LENGTH }), query(['id_quota']).optional().not().isArray().isInt({ min: CONSTANTS.MONGOOSE_VALIDATION.Org_policies_id_quota_min, max: CONSTANTS.MONGOOSE_VALIDATION.Org_policies_id_quota_max }).withMessage(errorMsgs.ID_QUOTA), - query(['name']).optional().isString().trim().escape().notEmpty(), + query(['name']).optional().isString().trim().notEmpty(), query(['active_roles.add']).optional().toArray() .custom(isFlatStringArray) .customSanitizer(toUpperCaseArray) @@ -392,7 +394,7 @@ router.get('/org/:shortname/id_quota', } */ mw.validateUser, - param(['shortname']).isString().trim().escape().notEmpty().isLength({ min: CONSTANTS.MIN_SHORTNAME_LENGTH, max: CONSTANTS.MAX_SHORTNAME_LENGTH }), + param(['shortname']).isString().trim().notEmpty().isLength({ min: CONSTANTS.MIN_SHORTNAME_LENGTH, max: CONSTANTS.MAX_SHORTNAME_LENGTH }), parseError, parseGetParams, controller.ORG_ID_QUOTA) @@ -464,7 +466,7 @@ router.get('/org/:shortname/users', } */ mw.validateUser, - param(['shortname']).isString().trim().escape().notEmpty().isLength({ min: CONSTANTS.MIN_SHORTNAME_LENGTH, max: CONSTANTS.MAX_SHORTNAME_LENGTH }), + param(['shortname']).isString().trim().notEmpty().isLength({ min: CONSTANTS.MIN_SHORTNAME_LENGTH, max: CONSTANTS.MAX_SHORTNAME_LENGTH }), query(['page']).optional().isInt({ min: CONSTANTS.PAGINATOR_PAGE }), parseError, parseGetParams, @@ -546,14 +548,14 @@ router.post('/org/:shortname/user', mw.validateUser, mw.onlySecretariatOrAdmin, mw.onlyOrgWithPartnerRole, - param(['shortname']).isString().trim().escape().notEmpty().isLength({ min: CONSTANTS.MIN_SHORTNAME_LENGTH, max: CONSTANTS.MAX_SHORTNAME_LENGTH }), - body(['username']).isString().trim().escape().notEmpty().custom(isValidUsername), - body(['org_uuid']).optional().isString().trim().escape(), - body(['uuid']).optional().isString().trim().escape(), - body(['name.first']).optional().isString().trim().escape().isLength({ max: CONSTANTS.MAX_FIRSTNAME_LENGTH }).withMessage(errorMsgs.FIRSTNAME_LENGTH), - body(['name.last']).optional().isString().trim().escape().isLength({ max: CONSTANTS.MAX_LASTNAME_LENGTH }).withMessage(errorMsgs.LASTNAME_LENGTH), - body(['name.middle']).optional().isString().trim().escape().isLength({ max: CONSTANTS.MAX_MIDDLENAME_LENGTH }).withMessage(errorMsgs.MIDDLENAME_LENGTH), - body(['name.suffix']).optional().isString().trim().escape().isLength({ max: CONSTANTS.MAX_SUFFIX_LENGTH }).withMessage(errorMsgs.SUFFIX_LENGTH), + param(['shortname']).isString().trim().notEmpty().isLength({ min: CONSTANTS.MIN_SHORTNAME_LENGTH, max: CONSTANTS.MAX_SHORTNAME_LENGTH }), + body(['username']).isString().trim().notEmpty().custom(isValidUsername), + body(['org_uuid']).optional().isString().trim(), + body(['uuid']).optional().isString().trim(), + body(['name.first']).optional().isString().trim().isLength({ max: CONSTANTS.MAX_FIRSTNAME_LENGTH }).withMessage(errorMsgs.FIRSTNAME_LENGTH), + body(['name.last']).optional().isString().trim().isLength({ max: CONSTANTS.MAX_LASTNAME_LENGTH }).withMessage(errorMsgs.LASTNAME_LENGTH), + body(['name.middle']).optional().isString().trim().isLength({ max: CONSTANTS.MAX_MIDDLENAME_LENGTH }).withMessage(errorMsgs.MIDDLENAME_LENGTH), + body(['name.suffix']).optional().isString().trim().isLength({ max: CONSTANTS.MAX_SUFFIX_LENGTH }).withMessage(errorMsgs.SUFFIX_LENGTH), body(['authority.active_roles']).optional() .custom(mw.isFlatStringArray) .customSanitizer(toUpperCaseArray) @@ -629,8 +631,8 @@ router.get('/org/:shortname/user/:username', } */ mw.validateUser, - param(['shortname']).isString().trim().escape().notEmpty().isLength({ min: CONSTANTS.MIN_SHORTNAME_LENGTH, max: CONSTANTS.MAX_SHORTNAME_LENGTH }), - param(['username']).isString().trim().escape().notEmpty().custom(isValidUsername), + param(['shortname']).isString().trim().notEmpty().isLength({ min: CONSTANTS.MIN_SHORTNAME_LENGTH, max: CONSTANTS.MAX_SHORTNAME_LENGTH }), + param(['username']).isString().trim().notEmpty().custom(isValidUsername), parseError, parseGetParams, controller.USER_SINGLE) @@ -717,15 +719,17 @@ router.put('/org/:shortname/user/:username', return mw.validateQueryParameterNames(query, ['active', 'new_username', 'org_short_name', 'name.first', 'name.last', 'name.middle', 'name.suffix', 'active_roles.add', 'active_roles.remove']) }), - param(['shortname']).isString().trim().escape().notEmpty().isLength({ min: CONSTANTS.MIN_SHORTNAME_LENGTH, max: CONSTANTS.MAX_SHORTNAME_LENGTH }), - param(['username']).isString().trim().escape().notEmpty().custom(isValidUsername), + query(['active', 'new_username', 'org_short_name', 'name.first', 'name.last', 'name.middle', + 'name.suffix', 'active_roles.add', 'active_roles.remove']).custom((val) => { return mw.containsNoInvalidCharacters(val) }), + param(['shortname']).isString().trim().notEmpty().isLength({ min: CONSTANTS.MIN_SHORTNAME_LENGTH, max: CONSTANTS.MAX_SHORTNAME_LENGTH }), + param(['username']).isString().trim().notEmpty().custom(isValidUsername), query(['active']).optional().isBoolean({ loose: true }), - query(['new_username']).optional().isString().trim().escape().notEmpty().custom(isValidUsername), - query(['org_short_name']).optional().isString().trim().escape().notEmpty().isLength({ min: CONSTANTS.MIN_SHORTNAME_LENGTH, max: CONSTANTS.MAX_SHORTNAME_LENGTH }), - body(['name.first']).optional().isString().trim().escape().isLength({ max: CONSTANTS.MAX_FIRSTNAME_LENGTH }).withMessage(errorMsgs.FIRSTNAME_LENGTH), - body(['name.last']).optional().isString().trim().escape().isLength({ max: CONSTANTS.MAX_LASTNAME_LENGTH }).withMessage(errorMsgs.LASTNAME_LENGTH), - body(['name.middle']).optional().isString().trim().escape().isLength({ max: CONSTANTS.MAX_MIDDLENAME_LENGTH }).withMessage(errorMsgs.MIDDLENAME_LENGTH), - body(['name.suffix']).optional().isString().trim().escape().isLength({ max: CONSTANTS.MAX_SUFFIX_LENGTH }).withMessage(errorMsgs.SUFFIX_LENGTH), + query(['new_username']).optional().isString().trim().notEmpty().custom(isValidUsername), + query(['org_short_name']).optional().isString().trim().notEmpty().isLength({ min: CONSTANTS.MIN_SHORTNAME_LENGTH, max: CONSTANTS.MAX_SHORTNAME_LENGTH }), + body(['name.first']).optional().isString().trim().isLength({ max: CONSTANTS.MAX_FIRSTNAME_LENGTH }).withMessage(errorMsgs.FIRSTNAME_LENGTH), + body(['name.last']).optional().isString().trim().isLength({ max: CONSTANTS.MAX_LASTNAME_LENGTH }).withMessage(errorMsgs.LASTNAME_LENGTH), + body(['name.middle']).optional().isString().trim().isLength({ max: CONSTANTS.MAX_MIDDLENAME_LENGTH }).withMessage(errorMsgs.MIDDLENAME_LENGTH), + body(['name.suffix']).optional().isString().trim().isLength({ max: CONSTANTS.MAX_SUFFIX_LENGTH }).withMessage(errorMsgs.SUFFIX_LENGTH), query(['active_roles.add']).optional().toArray() .custom(isFlatStringArray) .customSanitizer(toUpperCaseArray) @@ -807,8 +811,8 @@ router.put('/org/:shortname/user/:username/reset_secret', */ mw.validateUser, mw.onlyOrgWithPartnerRole, - param(['shortname']).isString().trim().escape().notEmpty().isLength({ min: CONSTANTS.MIN_SHORTNAME_LENGTH, max: CONSTANTS.MAX_SHORTNAME_LENGTH }), - param(['username']).isString().trim().escape().notEmpty().custom(isValidUsername), + param(['shortname']).isString().trim().notEmpty().isLength({ min: CONSTANTS.MIN_SHORTNAME_LENGTH, max: CONSTANTS.MAX_SHORTNAME_LENGTH }), + param(['username']).isString().trim().notEmpty().custom(isValidUsername), parseError, parsePostParams, controller.USER_RESET_SECRET) diff --git a/src/controller/org.controller/org.controller.js b/src/controller/org.controller/org.controller.js index 62e38fe3..d1a094b5 100644 --- a/src/controller/org.controller/org.controller.js +++ b/src/controller/org.controller/org.controller.js @@ -9,7 +9,6 @@ const uuid = require('uuid') const errors = require('./error') const error = new errors.OrgControllerError() const validateUUID = require('uuid').validate -const decodeEntities = require('html-entities').decode const booleanIsTrue = require('../../utils/utils').booleanIsTrue /** @@ -246,11 +245,11 @@ async function createOrg (req, res, next) { switch (key) { case 'short_name': - newOrg.short_name = decodeEntities(req.ctx.body.short_name) + newOrg.short_name = req.ctx.body.short_name break case 'name': - newOrg.name = decodeEntities(req.ctx.body.name) + newOrg.name = req.ctx.body.name break case 'authority': @@ -342,10 +341,10 @@ async function updateOrg (req, res, next) { const key = k.toLowerCase() if (key === 'new_short_name') { - newOrg.short_name = decodeEntities(req.ctx.query.new_short_name) + newOrg.short_name = req.ctx.query.new_short_name agt = setAggregateOrgObj({ short_name: newOrg.short_name }) } else if (key === 'name') { - newOrg.name = decodeEntities(req.ctx.query.name) + newOrg.name = req.ctx.query.name } else if (key === 'id_quota') { newOrg.policies.id_quota = req.ctx.query.id_quota } else if (key === 'active_roles.add') { @@ -462,16 +461,16 @@ async function createUser (req, res, next) { } } else if (key === 'name') { if (req.ctx.body.name.first) { - newUser.name.first = decodeEntities(req.ctx.body.name.first) + newUser.name.first = req.ctx.body.name.first } if (req.ctx.body.name.last) { - newUser.name.last = decodeEntities(req.ctx.body.name.last) + newUser.name.last = req.ctx.body.name.last } if (req.ctx.body.name.middle) { - newUser.name.middle = decodeEntities(req.ctx.body.name.middle) + newUser.name.middle = req.ctx.body.name.middle } if (req.ctx.body.name.suffix) { - newUser.name.suffix = decodeEntities(req.ctx.body.name.suffix) + newUser.name.suffix = req.ctx.body.name.suffix } } else if (key === 'org_uuid') { return res.status(400).json(error.uuidProvided('org')) @@ -545,7 +544,6 @@ async function updateUser (req, res, next) { const shortName = req.ctx.params.shortname const newUser = new User() let newOrgShortName = null - let changesRequirePrivilegedRole = false // Set variable to true if protected fields are being modified const removeRoles = [] const addRoles = [] const userRepo = req.ctx.repositories.getUserRepository() @@ -585,36 +583,54 @@ async function updateUser (req, res, next) { newUser.name.middle = user.name.middle newUser.name.suffix = user.name.suffix + const queryParameterPermissions = { + new_username: true, + org_short_name: true, + 'name.first': false, + 'name.last': false, + 'name.middle': false, + 'name.suffix': false, + active: true, + 'active_roles.add': true, + 'active_roles.remove': true + + } + + // Specific check for org_short_name + if (Object.keys(req.ctx.query).length > 0 && Object.keys(req.ctx.query).includes('org_short_name') && !(isSecretariat)) { + logger.info({ uuid: req.ctx.uuid, message: 'The user could not be updated because ' + requesterUsername + ' is an Org Admin and tried to reassign the organization.' }) + return res.status(403).json(error.notAllowedToChangeOrganization()) + } + + // Check to ensure that the user has the right permissions to edit the fields tha they are requesting to edit, and fail fast if they do not. + if (Object.keys(req.ctx.query).length > 0 && Object.keys(req.ctx.query).some((key) => { return queryParameterPermissions[key] }) && !(isAdmin || isSecretariat)) { + logger.info({ uuid: req.ctx.uuid, message: 'The user could not be updated because ' + requesterUsername + ' user is not Org Admin or Secretariat to modify these fields.' }) + console.log('in failed admin') + return res.status(403).json(error.notOrgAdminOrSecretariatUpdate()) + } + for (const k in req.ctx.query) { const key = k.toLowerCase() if (key === 'new_username') { newUser.username = req.ctx.query.new_username - changesRequirePrivilegedRole = true } else if (key === 'org_short_name') { newOrgShortName = req.ctx.query.org_short_name - changesRequirePrivilegedRole = true - if (!isSecretariat) { - logger.info({ uuid: req.ctx.uuid, message: 'The user could not be updated because ' + requesterUsername + ' is an Org Admin and tried to reassign the organization.' }) - return res.status(403).json(error.notAllowedToChangeOrganization()) - } } else if (key === 'name.first') { - newUser.name.first = decodeEntities(req.ctx.query['name.first']) + newUser.name.first = req.ctx.query['name.first'] } else if (key === 'name.last') { - newUser.name.last = decodeEntities(req.ctx.query['name.last']) + newUser.name.last = req.ctx.query['name.last'] } else if (key === 'name.middle') { - newUser.name.middle = decodeEntities(req.ctx.query['name.middle']) + newUser.name.middle = req.ctx.query['name.middle'] } else if (key === 'name.suffix') { - newUser.name.suffix = decodeEntities(req.ctx.query['name.suffix']) + newUser.name.suffix = req.ctx.query['name.suffix'] } else if (key === 'active') { newUser.active = booleanIsTrue(req.ctx.query.active) - changesRequirePrivilegedRole = true } else if (key === 'active_roles.add') { if (Array.isArray(req.ctx.query['active_roles.add'])) { req.ctx.query['active_roles.add'].forEach(r => { addRoles.push(r) }) - changesRequirePrivilegedRole = true } } else if (key === 'active_roles.remove') { if (Array.isArray(req.ctx.query['active_roles.remove'])) { @@ -625,17 +641,10 @@ async function updateUser (req, res, next) { } removeRoles.push(r) } - changesRequirePrivilegedRole = true } } } - // Check for correct privileges if the requested changes require them - if (changesRequirePrivilegedRole && !(isAdmin || isSecretariat)) { - logger.info({ uuid: req.ctx.uuid, message: 'The user could not be updated because ' + requesterUsername + ' user is not Org Admin or Secretariat to modify these fields.' }) - return res.status(403).json(error.notOrgAdminOrSecretariatUpdate()) - } - // check if the new org exist if (newOrgShortName) { newUser.org_UUID = await orgRepo.getOrgUUID(newOrgShortName) diff --git a/src/controller/user.controller/index.js b/src/controller/user.controller/index.js index fb57a720..5d153266 100644 --- a/src/controller/user.controller/index.js +++ b/src/controller/user.controller/index.js @@ -75,6 +75,7 @@ router.get('/users', mw.validateUser, mw.onlySecretariat, query(['page']).optional().isInt({ min: CONSTANTS.PAGINATOR_PAGE }), + query(['page']).custom((val) => { return mw.containsNoInvalidCharacters(val) }), parseError, parseGetParams, controller.ALL_USERS) diff --git a/src/middleware/middleware.js b/src/middleware/middleware.js index 8e1ff53f..066743c3 100644 --- a/src/middleware/middleware.js +++ b/src/middleware/middleware.js @@ -439,6 +439,19 @@ function toUpperCaseArray (val) { return newArr } +// Check for the invalid characters <, >, and " +function containsNoInvalidCharacters (val) { + const invalidCharacterList = ['<', '>', '"'] + if (val) { + for (const invalidCharacter of invalidCharacterList) { + if (val.includes(invalidCharacter)) { + throw new Error('contains invalid character: ' + invalidCharacter) + } + } + } + return true +} + module.exports = { setCacheControl, optionallyValidateUser, @@ -457,5 +470,6 @@ module.exports = { validateJsonSyntax, rateLimiter: limiter, isFlatStringArray, - toUpperCaseArray + toUpperCaseArray, + containsNoInvalidCharacters } diff --git a/test/integration-tests/constants.js b/test/integration-tests/constants.js index 4b8b954b..6aab5359 100644 --- a/test/integration-tests/constants.js +++ b/test/integration-tests/constants.js @@ -241,6 +241,34 @@ const testAdp2 = { } } +const testOrg = { + + short_name: 'test_org', + name: 'Test Organization', + authority: { + active_roles: [ + 'CNA' + ] + }, + policies: { + id_quota: 100000 + } +} + +const existingOrg = { + + short_name: 'win_5', + name: 'Test Organization', + authority: { + active_roles: [ + 'CNA' + ] + }, + policies: { + id_quota: 100000 + } +} + module.exports = { headers, nonSecretariatUserHeaders, @@ -249,5 +277,7 @@ module.exports = { testCve, testCveEdited, testAdp, - testAdp2 + testAdp2, + testOrg, + existingOrg } diff --git a/test/integration-tests/org/postOrgTest.js b/test/integration-tests/org/postOrgTest.js new file mode 100644 index 00000000..08517dd3 --- /dev/null +++ b/test/integration-tests/org/postOrgTest.js @@ -0,0 +1,56 @@ +/* eslint-disable no-unused-expressions */ +const chai = require('chai') +chai.use(require('chai-http')) +const expect = chai.expect + +const constants = require('../constants.js') +const app = require('../../../src/index.js') + +describe('Testing Org post endpoint', () => { + context('Positive Tests', () => { + it('Allows creation of org', async () => { + await chai.request(app) + .post('/api/org') + .set({ ...constants.headers }) + .send(constants.testOrg) + .then((res, err) => { + expect(err).to.be.undefined + expect(res).to.have.status(200) + + expect(res.body).to.haveOwnProperty('message') + expect(res.body.message).to.equal(constants.testOrg.short_name + ' organization was successfully created.') + + expect(res.body).to.haveOwnProperty('created') + + expect(res.body.created).to.haveOwnProperty('name') + expect(res.body.created.name).to.equal(constants.testOrg.name) + + expect(res.body.created).to.haveOwnProperty('short_name') + expect(res.body.created.short_name).to.equal(constants.testOrg.short_name) + + expect(res.body.created).to.haveOwnProperty('UUID') + + expect(res.body.created).to.haveOwnProperty('policies') + expect(res.body.created.policies).to.deep.equal(constants.testOrg.policies) + + expect(res.body.created).to.haveOwnProperty('authority') + expect(res.body.created.authority).to.deep.equal(constants.testOrg.authority) + }) + }) + }) + context('Negitive Test', () => { + it('Should fail to create an org that already exists ', async () => { + await chai.request(app) + .post('/api/org') + .set({ ...constants.headers }) + .send(constants.existingOrg) + .then((res, err) => { + expect(err).to.be.undefined + expect(res).to.have.status(400) + + expect(res.body).to.haveOwnProperty('error') + expect(res.body.error).to.equal('ORG_EXISTS') + }) + }) + }) +}) diff --git a/test/integration-tests/user/createUserTest.js b/test/integration-tests/user/createUserTest.js index 7de53d70..45c32f86 100644 --- a/test/integration-tests/user/createUserTest.js +++ b/test/integration-tests/user/createUserTest.js @@ -21,6 +21,20 @@ const body = { active_roles: ['Admin'] } } + +const nonAdminBody = { + username: 'nonAdminUser', + active: 'true', + name: { + first: 'TestCnaAdmin', + last: 'test', + middle: 'N', + suffix: 'I' + }, + authority: { + } +} + describe('Testing create user endpoint', () => { it('Should return 200 and new user', (done) => { chai.request(app) @@ -35,4 +49,17 @@ describe('Testing create user endpoint', () => { done() }) }) + it('Should return 200 and create a non admin user', (done) => { + chai.request(app) + .post('/api/org/range_4/user') + .set(constants.headers) + .send(nonAdminBody) + .end((err, res) => { + expect(err).to.be.null + expect(res.body).to.have.property('created') + expect(res.body.created.username).to.equal(nonAdminBody.username) + expect(res).to.have.status(200) + done() + }) + }) }) diff --git a/test/integration-tests/user/updateUserTest.js b/test/integration-tests/user/updateUserTest.js new file mode 100644 index 00000000..f25d55f7 --- /dev/null +++ b/test/integration-tests/user/updateUserTest.js @@ -0,0 +1,33 @@ +/* eslint-disable no-unused-expressions */ + +const chai = require('chai') +chai.use(require('chai-http')) + +const expect = chai.expect + +const constants = require('../constants.js') +const app = require('../../../src/index.js') + +describe('Testing Edit user endpoint', () => { + context('User edit tests', () => { + it('Should return 200 when only name changes are done', async () => { + await chai.request(app) + .put('/api/org/win_5/user/jasminesmith@win_5.com?name.first=NewName') + .set(constants.nonSecretariatUserHeaders) + .then((res, err) => { + expect(err).to.be.undefined + expect(res).to.have.status(200) + }) + }) + it('Should return an error when admin is required', async () => { + await chai.request(app) + .put('/api/org/win_5/user/jasminesmith@win_5.com?new_username=NewUsername') + .set(constants.nonSecretariatUserHeaders) + .then((res, err) => { + expect(err).to.be.undefined + expect(res).to.have.status(403) + expect(res.body.error).to.contain('NOT_ORG_ADMIN_OR_SECRETARIAT_UPDATE') + }) + }) + }) +}) diff --git a/test/unit-tests/middleware/checkForInvalidCharacters.js b/test/unit-tests/middleware/checkForInvalidCharacters.js new file mode 100644 index 00000000..a03a94cd --- /dev/null +++ b/test/unit-tests/middleware/checkForInvalidCharacters.js @@ -0,0 +1,35 @@ +const chai = require('chai') +const expect = chai.expect + +const { containsNoInvalidCharacters } = require('../../../src/middleware/middleware') + +describe('Testing Invalid Character checker', () => { + context('negative tests', () => { + it('Should fail when detecting a >', async () => { + try { + containsNoInvalidCharacters('sa>fe') + } catch (err) { + expect(err.message).to.contain('contains invalid character: >') + } + }) + it('Should fail when detecting a <', async () => { + try { + containsNoInvalidCharacters('sa { + try { + containsNoInvalidCharacters('sa"fe') + } catch (err) { + expect(err.message).to.contain('contains invalid character: "') + } + }) + }) + context('positive test', () => { + it('Should pass if no invalid characters', async () => { + containsNoInvalidCharacters('safe') + }) + }) +})