diff --git a/server/package-lock.json b/server/package-lock.json index 5f40d7a..63103cb 100644 --- a/server/package-lock.json +++ b/server/package-lock.json @@ -21,7 +21,8 @@ "pm2": "^5.2.0", "sqlite": "^4.0.23", "sqlite3": "^5.1.1", - "ts-node": "^10.9.1" + "ts-node": "^10.9.1", + "validator": "^13.9.0" }, "devDependencies": { "@types/cors": "^2.8.12", @@ -29,6 +30,7 @@ "@types/jest": "^28.1.8", "@types/jsonwebtoken": "^8.5.8", "@types/supertest": "^2.0.12", + "@types/validator": "^13.7.17", "jest": "^29.0.3", "supertest": "^6.2.4", "ts-jest": "^29.0.0", @@ -2125,6 +2127,12 @@ "@types/superagent": "*" } }, + "node_modules/@types/validator": { + "version": "13.7.17", + "resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.7.17.tgz", + "integrity": "sha512-aqayTNmeWrZcvnG2MG9eGYI6b7S5fl+yKgPs6bAjOTwPS316R5SxBGKvtSExfyoJU7pIeHJfsHI0Ji41RVMkvQ==", + "dev": true + }, "node_modules/@types/yargs": { "version": "17.0.10", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.10.tgz", @@ -8771,6 +8779,14 @@ "node": ">=10.12.0" } }, + "node_modules/validator": { + "version": "13.9.0", + "resolved": "https://registry.npmjs.org/validator/-/validator-13.9.0.tgz", + "integrity": "sha512-B+dGG8U3fdtM0/aNK4/X8CXq/EcxU2WPrPEkJGslb47qyHsxmbggTWK0yEA4qnYVNF+nxNlN88o14hIcPmSIEA==", + "engines": { + "node": ">= 0.10" + } + }, "node_modules/vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", @@ -10697,6 +10713,12 @@ "@types/superagent": "*" } }, + "@types/validator": { + "version": "13.7.17", + "resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.7.17.tgz", + "integrity": "sha512-aqayTNmeWrZcvnG2MG9eGYI6b7S5fl+yKgPs6bAjOTwPS316R5SxBGKvtSExfyoJU7pIeHJfsHI0Ji41RVMkvQ==", + "dev": true + }, "@types/yargs": { "version": "17.0.10", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.10.tgz", @@ -15713,6 +15735,11 @@ "convert-source-map": "^1.6.0" } }, + "validator": { + "version": "13.9.0", + "resolved": "https://registry.npmjs.org/validator/-/validator-13.9.0.tgz", + "integrity": "sha512-B+dGG8U3fdtM0/aNK4/X8CXq/EcxU2WPrPEkJGslb47qyHsxmbggTWK0yEA4qnYVNF+nxNlN88o14hIcPmSIEA==" + }, "vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", diff --git a/server/package.json b/server/package.json index 2d051e1..f2a2e58 100644 --- a/server/package.json +++ b/server/package.json @@ -25,7 +25,8 @@ "pm2": "^5.2.0", "sqlite": "^4.0.23", "sqlite3": "^5.1.1", - "ts-node": "^10.9.1" + "ts-node": "^10.9.1", + "validator": "^13.9.0" }, "devDependencies": { "@types/cors": "^2.8.12", @@ -33,6 +34,7 @@ "@types/jest": "^28.1.8", "@types/jsonwebtoken": "^8.5.8", "@types/supertest": "^2.0.12", + "@types/validator": "^13.7.17", "jest": "^29.0.3", "supertest": "^6.2.4", "ts-jest": "^29.0.0", diff --git a/server/src/handlers/userHandler.ts b/server/src/handlers/userHandler.ts index 6f29209..e8700b3 100644 --- a/server/src/handlers/userHandler.ts +++ b/server/src/handlers/userHandler.ts @@ -17,6 +17,7 @@ import crypto from 'crypto'; import { signJwt } from '../auth'; import { Datastore } from '../datastore'; import { ExpressHandler, ExpressHandlerWithParams } from '../types'; +import validator from 'validator' export class UserHandler { private db: Datastore; @@ -57,6 +58,12 @@ export class UserHandler { return res.status(400).send({ error: ERRORS.USER_REQUIRED_FIELDS }); } + if (!validator.isEmail(email)) return res.status(400).send({ error: ERRORS.INVALID_EMAIL}); + if (!validator.isAlphanumeric(userName)) return res.status(400).send({ error: ERRORS.INVALID_USERNAME}); + if (!validator.isStrongPassword(password)) return res.status(400).send({ error: ERRORS.WEAK_PASSWORD}); + if (firstName && !validator.isAlphanumeric(firstName)) return res.status(400).send({ error: ERRORS.INVALID_FIRSTNAME}); + if (lastName && !validator.isAlphanumeric(lastName)) return res.status(400).send({ error: ERRORS.INVALID_LASTNAME}); + if (await this.db.getUserByEmail(email)) { return res.status(403).send({ error: ERRORS.DUPLICATE_EMAIL }); } @@ -121,6 +128,8 @@ export class UserHandler { const currentUserId = res.locals.userId; const { userName } = req.body; + if (userName && !validator.isAlphanumeric(userName)) return res.status(400).send({ error: ERRORS.INVALID_USERNAME}); + if (userName && (await this.isDuplicateUserName(currentUserId, userName))) { return res.status(403).send({ error: ERRORS.DUPLICATE_USERNAME }); } diff --git a/shared/src/errors.ts b/shared/src/errors.ts index cd2c451..ea56858 100644 --- a/shared/src/errors.ts +++ b/shared/src/errors.ts @@ -6,6 +6,11 @@ export enum ERRORS { USER_REQUIRED_FIELDS = 'Email, username, and password are required', DUPLICATE_EMAIL = 'An account with this email already exists', DUPLICATE_USERNAME = 'An account with this username already exists', + INVALID_EMAIL = 'Invalid email', + WEAK_PASSWORD = 'Weak password', + INVALID_USERNAME = 'Username must have only letters and numbers', + INVALID_FIRSTNAME = 'First name must have only letters and numbers', + INVALID_LASTNAME = 'Last name must have only letters and numbers', POST_ID_MISSING = 'Post ID is missing', POST_NOT_FOUND = 'Post not found',