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;