diff --git a/plugins/htpasswd/src/crypt3.ts b/plugins/htpasswd/src/crypt3.ts index b32bb94e..9e34f98c 100644 --- a/plugins/htpasswd/src/crypt3.ts +++ b/plugins/htpasswd/src/crypt3.ts @@ -16,8 +16,12 @@ import crypt from 'unix-crypt-td-js'; * distros), sha256 or sha512. Default is sha512. * @returns {string} Generated salt string */ -export function createSalt(type = 'sha512'): string { +export function createSalt(type = 'crypt'): string { switch (type) { + case 'crypt': + // Legacy crypt salt with no prefix (only the first 2 bytes will be used). + return crypto.randomBytes(2).toString('base64'); + case 'md5': return '$1$' + crypto.randomBytes(10).toString('base64'); diff --git a/plugins/htpasswd/src/utils.ts b/plugins/htpasswd/src/utils.ts index 1198b0ad..8ff0a6e0 100644 --- a/plugins/htpasswd/src/utils.ts +++ b/plugins/htpasswd/src/utils.ts @@ -159,21 +159,21 @@ export function getCryptoPassword(password: string): string { * @returns {string} */ export function changePasswordToHTPasswd(body: string, user: string, passwd: string, newPasswd: string): string { - let _passwd; - let _newPasswd; - if (crypt3) { - _passwd = crypt3(passwd); - _newPasswd = crypt3(newPasswd); - } else { - _passwd = getCryptoPassword(passwd); - _newPasswd = getCryptoPassword(newPasswd); - } - let lines = body.split('\n'); lines = lines.map(line => { const [username, password] = line.split(':', 3); if (username === user) { + let _passwd; + let _newPasswd; + if (crypt3) { + _passwd = crypt3(passwd, password); + _newPasswd = crypt3(newPasswd); + } else { + _passwd = getCryptoPassword(passwd); + _newPasswd = getCryptoPassword(newPasswd); + } + if (password == _passwd) { // replace old password hash with new password hash line = line.replace(_passwd, _newPasswd); diff --git a/plugins/htpasswd/tests/crypt3.test.ts b/plugins/htpasswd/tests/crypt3.test.ts index 8f3f65e6..61955570 100644 --- a/plugins/htpasswd/tests/crypt3.test.ts +++ b/plugins/htpasswd/tests/crypt3.test.ts @@ -12,6 +12,7 @@ jest.mock('crypto', () => { describe('createSalt', () => { test('should match with the correct salt type', () => { + expect(createSalt('crypt')).toEqual('/UEGzD0RxSNDZA=='); expect(createSalt('md5')).toEqual('$1$/UEGzD0RxSNDZA=='); expect(createSalt('blowfish')).toEqual('$2a$/UEGzD0RxSNDZA=='); expect(createSalt('sha256')).toEqual('$5$/UEGzD0RxSNDZA=='); @@ -23,4 +24,8 @@ describe('createSalt', () => { createSalt('bad'); }).toThrow(/Unknown salt type at crypt3.createSalt: bad/); }); + + test('should generate legacy crypt salt by default', () => { + expect(createSalt()).toEqual(createSalt('crypt')); + }); }); diff --git a/plugins/htpasswd/tests/htpasswd.test.ts b/plugins/htpasswd/tests/htpasswd.test.ts index 45702a69..d50c8b29 100644 --- a/plugins/htpasswd/tests/htpasswd.test.ts +++ b/plugins/htpasswd/tests/htpasswd.test.ts @@ -1,3 +1,4 @@ +import crypto from 'crypto'; // @ts-ignore import fs from 'fs'; @@ -22,6 +23,12 @@ describe('HTPasswd', () => { beforeEach(() => { wrapper = new HTPasswd(config, (stuff as unknown) as VerdaccioConfigApp); jest.resetModules(); + + crypto.randomBytes = jest.fn(() => { + return { + toString: (): string => '$6', + }; + }); }); describe('constructor()', () => { diff --git a/plugins/htpasswd/tests/utils.test.ts b/plugins/htpasswd/tests/utils.test.ts index 74ab773c..09b1683c 100644 --- a/plugins/htpasswd/tests/utils.test.ts +++ b/plugins/htpasswd/tests/utils.test.ts @@ -1,3 +1,5 @@ +import crypto from 'crypto'; + import { verifyPassword, lockAndRead, @@ -89,6 +91,12 @@ describe('addUserToHTPasswd - crypt3', () => { toJSON: (): string => '2018-01-14T11:17:40.712Z', }; }); + + crypto.randomBytes = jest.fn(() => { + return { + toString: (): string => '$6', + }; + }); }); it('should add new htpasswd to the end', () => { @@ -222,6 +230,20 @@ describe('changePasswordToHTPasswd', () => { expect(changePasswordToHTPasswd(body, 'root', 'demo123', 'newPassword')).toMatchSnapshot(); }); + test('should generate a different result on salt change', () => { + crypto.randomBytes = jest.fn(() => { + return { + toString: (): string => 'AB', + }; + }); + + const body = 'root:$6qLTHoPfGLy2:autocreated 2018-08-20T13:38:12.164Z'; + + expect(changePasswordToHTPasswd(body, 'root', 'demo123', 'demo123')).toEqual( + 'root:ABfaAAjDKIgfw:autocreated 2018-08-20T13:38:12.164Z' + ); + }); + test('should change the password when crypt3 is not available', () => { jest.resetModules(); jest.doMock('../src/crypt3.ts', () => false);