diff --git a/src/lib/error/password-undefined.ts b/src/lib/error/password-undefined.ts new file mode 100644 index 0000000000..5709d2f405 --- /dev/null +++ b/src/lib/error/password-undefined.ts @@ -0,0 +1,23 @@ +export default class PasswordUndefinedError extends Error { + constructor() { + super(); + Error.captureStackTrace(this, this.constructor); + + this.name = this.constructor.name; + this.message = 'Password cannot be empty or undefined'; + } + + toJSON(): any { + const obj = { + isJoi: true, + name: this.constructor.name, + details: [ + { + validationErrors: [], + message: 'Password cannot be empty or undefined', + }, + ], + }; + return obj; + } +} diff --git a/src/lib/routes/admin-api/util.js b/src/lib/routes/admin-api/util.js index 2b1d342c27..3f84a1d4f0 100644 --- a/src/lib/routes/admin-api/util.js +++ b/src/lib/routes/admin-api/util.js @@ -67,6 +67,11 @@ const handleErrors = (res, logger, error) => { .status(400) .json(error) .end(); + case 'PasswordUndefinedError': + return res + .status(400) + .json(error) + .end(); default: logger.error('Server failed executing request', error); return res.status(500).end(); diff --git a/src/lib/services/user-service.ts b/src/lib/services/user-service.ts index 63fd394d5e..6a1143f27a 100644 --- a/src/lib/services/user-service.ts +++ b/src/lib/services/user-service.ts @@ -18,6 +18,7 @@ import { IUnleashConfig } from '../types/option'; import SessionService from './session-service'; import { IUnleashServices } from '../types/services'; import { IUnleashStores } from '../types/stores'; +import PasswordUndefinedError from '../error/password-undefined'; export interface ICreateUser { name?: string; @@ -94,10 +95,14 @@ class UserService { } validatePassword(password: string): boolean { - const result = owasp.test(password); - if (!result.strong) { - throw new OwaspValidationError(result); - } else return true; + if (password) { + const result = owasp.test(password); + if (!result.strong) { + throw new OwaspValidationError(result); + } else return true; + } else { + throw new PasswordUndefinedError(); + } } async initAdminUser(): Promise { diff --git a/src/test/e2e/api/auth/reset-password-controller.e2e.test.ts b/src/test/e2e/api/auth/reset-password-controller.e2e.test.ts index 2be5f42a53..ba5687fa62 100644 --- a/src/test/e2e/api/auth/reset-password-controller.e2e.test.ts +++ b/src/test/e2e/api/auth/reset-password-controller.e2e.test.ts @@ -257,3 +257,32 @@ test.serial( .expect(res => t.is(res.status, 401)); }, ); + +test.serial( + 'Trying to change password to undefined should yield 400 without crashing the server', + async t => { + t.plan(0); + const request = await setupApp(stores); + const url = await resetTokenService.createResetPasswordUrl( + user.id, + adminUser.username, + ); + const relative = getBackendResetUrl(url); + let token; + await request + .get(relative) + .expect(200) + .expect('Content-Type', /json/) + .expect(res => { + token = res.body.token; + }); + await request + .post('/auth/reset/password') + .send({ + email: user.email, + token, + password: undefined, + }) + .expect(400); + }, +);