diff --git a/__tests__/cli.test.js b/__tests__/cli.test.js index 35f14ec..e7eed4b 100644 --- a/__tests__/cli.test.js +++ b/__tests__/cli.test.js @@ -22,7 +22,7 @@ function runCLI(args) { describe('CLI - Basic Usage', () => { test('masks with default settings', () => { const result = runCLI('"mysecretkey"'); - expect(result.stdout).toBe('mys*****key'); + expect(result.stdout).toBe('mys******ey'); expect(result.exitCode).toBe(0); }); @@ -49,13 +49,13 @@ describe('CLI - Basic Usage', () => { describe('CLI - Options', () => { test('respects --prefix option', () => { const result = runCLI('"mysecretkey" --prefix 2'); - expect(result.stdout).toBe('my*******key'); + expect(result.stdout).toBe('my******key'); expect(result.exitCode).toBe(0); }); test('respects -p short option', () => { const result = runCLI('"mysecretkey" -p 2'); - expect(result.stdout).toBe('my*******key'); + expect(result.stdout).toBe('my******key'); expect(result.exitCode).toBe(0); }); @@ -79,7 +79,7 @@ describe('CLI - Options', () => { test('respects -c short option', () => { const result = runCLI('"teststring" -c "#"'); - expect(result.stdout).toBe('tes####ring'); + expect(result.stdout).toBe('tes####ing'); expect(result.exitCode).toBe(0); }); @@ -125,7 +125,7 @@ describe('CLI - Advanced Features', () => { test('percentage mask', () => { const result = runCLI('"1234567890" --percentage 50'); - expect(result.stdout).toBe('12***67890'); + expect(result.stdout).toBe('12*****890'); expect(result.exitCode).toBe(0); }); @@ -159,7 +159,7 @@ describe('CLI - Error Handling', () => { describe('CLI - Special Characters', () => { test('handles strings with spaces', () => { const result = runCLI('"my secret key"'); - expect(result.stdout).toBe('my ****t key'); + expect(result.stdout).toBe('my *******key'); expect(result.exitCode).toBe(0); }); diff --git a/__tests__/index.test.js b/__tests__/index.test.js index 9194b80..7cdf517 100644 --- a/__tests__/index.test.js +++ b/__tests__/index.test.js @@ -7,7 +7,7 @@ const { describe('obscureString - Basic Functionality', () => { test('masks the middle with default settings', () => { const result = obscureString('mysecretkey'); - expect(result).toBe('mys*****key'); + expect(result).toBe('mys******ey'); }); test('masks with custom prefix/suffix and mask character', () => { @@ -45,7 +45,7 @@ describe('obscureString - Enhanced Input Validation', () => { test('coerces numbers to strings', () => { expect(obscureString(12345)).toBe('12345'); // Too short - expect(obscureString(1234567890)).toBe('123****890'); + expect(obscureString(1234567890)).toBe('123*****90'); }); test('coerces booleans to strings', () => { @@ -69,8 +69,8 @@ describe('obscureString - Enhanced Input Validation', () => { const result = obscureString(longString); expect(result.length).toBe(10000); expect(result.startsWith('aaa')).toBe(true); - expect(result.endsWith('aaa')).toBe(true); - expect(result.slice(3, -3)).toBe('*'.repeat(9994)); + expect(result.endsWith('aa')).toBe(true); + expect(result.slice(3, -2)).toBe('*'.repeat(9995)); }); test('throws error for strings exceeding maxLength', () => { @@ -108,23 +108,23 @@ describe('obscureString - Enhanced Input Validation', () => { describe('obscureString - Unicode & Special Characters', () => { test('handles unicode emojis correctly', () => { const result = obscureString('πŸ”secretπŸ”‘'); - expect(result).toBe('πŸ”se***etπŸ”‘'); + expect(result).toBe('πŸ”se***tπŸ”‘'); }); test('handles multi-byte unicode characters', () => { const result = obscureString('γ“γ‚“γ«γ‘γ―δΈ–η•Œ'); - expect(result).toBe('こんに*δΈ–η•Œ'); + expect(result).toBe('こんに**δΈ–η•Œ'); }); test('handles mixed unicode and ASCII', () => { const result = obscureString('user@δΎ‹γˆ.com'); - expect(result).toBe('use****com'); + expect(result).toBe('use******om'); }); test('handles special characters', () => { - expect(obscureString('a!b@c#d$e%f^g')).toBe('a!b*****%f^g'); + expect(obscureString('a!b@c#d$e%f^g')).toBe('a!b********^g'); expect(obscureString('')).toBe( - '' + '' ); }); @@ -145,25 +145,25 @@ describe('obscureString - Security Edge Cases', () => { const result = obscureString(xss); expect(result).not.toContain('alert'); expect(result.startsWith('')).toBe(true); + expect(result.endsWith('t>')).toBe(true); }); test('handles SQL injection patterns', () => { const sql = "'; DROP TABLE users; --"; const result = obscureString(sql); - expect(result).toBe("'; ***************; --"); + expect(result).toBe("'; ******************--"); }); test('handles path traversal attempts', () => { const path = '../../etc/passwd'; const result = obscureString(path); - expect(result).toBe('../*********asswd'); + expect(result).toBe('../***********wd'); }); test('handles command injection attempts', () => { const cmd = 'test; rm -rf /'; const result = obscureString(cmd); - expect(result).toBe('tes*******f /'); + expect(result).toBe('tes********* /'); }); test('does not expose sensitive data in errors', () => { @@ -201,7 +201,7 @@ describe('obscureString - New Features: reverseMask', () => { test('reverseMask with default settings', () => { const result = obscureString('abcdefghijk', { reverseMask: true }); - expect(result).toBe('***defgh***'); + expect(result).toBe('***defghi**'); }); test('reverseMask with minMaskLength', () => { @@ -219,7 +219,7 @@ describe('obscureString - New Features: reverseMask', () => { describe('obscureString - New Features: percentage', () => { test('masks by percentage', () => { const result = obscureString('1234567890', { percentage: 50 }); - expect(result).toBe('12***67890'); + expect(result).toBe('12*****890'); }); test('masks 100% by percentage', () => { @@ -325,7 +325,7 @@ describe('obscureStringBatch', () => { test('handles array with mixed types', () => { const result = obscureStringBatch(['string', 123, null, undefined]); - expect(result[0]).toBe('str***ing'); + expect(result[0]).toBe('str*ng'); expect(result[1]).toBe('123'); // Too short expect(result[2]).toBe(''); expect(result[3]).toBe(''); @@ -343,11 +343,11 @@ describe('getMaskInfo', () => { expect(info).toEqual({ willBeMasked: true, originalLength: 11, - maskedLength: 5, - visibleChars: 6, - maskedChars: 5, + maskedLength: 6, + visibleChars: 5, + maskedChars: 6, prefixLength: 3, - suffixLength: 3, + suffixLength: 2, }); }); @@ -457,7 +457,7 @@ describe('Stress Tests', () => { const result = obscureString(veryLongString); expect(result.length).toBe(100000); expect(result.startsWith('aaa')).toBe(true); - expect(result.endsWith('aaa')).toBe(true); + expect(result.endsWith('aa')).toBe(true); }); test('handles many repeated calls', () => { diff --git a/src/index.js b/src/index.js index d544876..2c73f24 100755 --- a/src/index.js +++ b/src/index.js @@ -5,7 +5,7 @@ * @param {Object} options - Configuration options. * @param {string} [options.maskChar='*'] - Character to use for masking. * @param {number} [options.prefixLength=3] - Number of characters to show at the beginning. - * @param {number} [options.suffixLength=3] - Number of characters to show at the end. + * @param {number} [options.suffixLength=2] - Number of characters to show at the end. * @param {number} [options.minMaskLength=0] - Minimum number of mask characters to show (string must be long enough). * @param {boolean} [options.fullMask=false] - Mask the entire string. * @param {boolean} [options.reverseMask=false] - Show middle, hide edges. @@ -27,7 +27,7 @@ function obscureString(str, options = {}) { const { maskChar = '*', prefixLength = 3, - suffixLength = 3, + suffixLength = 2, minMaskLength = 0, fullMask = false, reverseMask = false, @@ -105,11 +105,13 @@ function obscureString(str, options = {}) { // Performance optimization: use string concatenation for small strings, // array join for larger ones (more efficient) + const suffix = suffixLength > 0 ? str.slice(-suffixLength) : ''; + if (strLength < 100) { return ( str.slice(0, prefixLength) + maskChar.repeat(maskLength) + - str.slice(-suffixLength) + suffix ); } @@ -117,7 +119,7 @@ function obscureString(str, options = {}) { return ( str.slice(0, prefixLength) + maskChar.repeat(maskLength) + - str.slice(strLength - suffixLength) + suffix ); } @@ -129,7 +131,7 @@ function applyPreset(str, preset, maskChar) { switch (preset.toLowerCase()) { case 'email': { const atIndex = str.lastIndexOf('@'); - if (atIndex <= 0) return obscureString(str, { maskChar }); + if (atIndex <= 0) return obscureString(str, { maskChar, prefixLength: 3, suffixLength: 0 }); const localPart = str.slice(0, atIndex); const domain = str.slice(atIndex); @@ -192,7 +194,7 @@ function maskByPercentage(str, percentage, maskChar) { return ( str.slice(0, prefixLen) + maskChar.repeat(charsToMask) + - str.slice(-suffixLen) + (suffixLen > 0 ? str.slice(-suffixLen) : '') ); } @@ -252,7 +254,7 @@ function getMaskInfo(str, options = {}) { const { prefixLength = 3, - suffixLength = 3, + suffixLength = 2, fullMask = false, minMaskLength = 0, } = options;