From 615b7f0f02ce396106b9a29351324cfbf9113de0 Mon Sep 17 00:00:00 2001 From: slippyvex Date: Fri, 17 May 2024 07:46:52 +0200 Subject: [PATCH] fixed issue #8 and bumped version to 1.1.3 --- src/lib/dataGuard.ts | 24 +++++++++++++++++++++--- tests/masking.test.ts | 5 +++++ 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/src/lib/dataGuard.ts b/src/lib/dataGuard.ts index f96b5a4..780d87f 100644 --- a/src/lib/dataGuard.ts +++ b/src/lib/dataGuard.ts @@ -56,8 +56,10 @@ const sensitiveContentRegExp = { passwordMention: /(?<=.*(password|passwd|pwd)(?:\s*:\s*|\s+))\S+/gi } as const; +const SKIP_MASKING_PATTERN = /##([^#]*)##/g; + function maskSensitiveValue(value: string, options: Partial): string { - const skipMaskingPattern = /##([^#]*)##/g; // Pattern to match content that should not be masked + const skipMaskingPattern = SKIP_MASKING_PATTERN; // Pattern to match content that should not be masked const maskLength = options?.maskLength || value.length - 4; const maskingChar = options?.maskingChar || '*'; // Skip masking if the entire value is wrapped in '##' @@ -97,7 +99,7 @@ function maskSensitiveContent( options?: Partial ): string { const allPatterns = { ...defaultPatterns, ...customPatterns }; - const skipMaskingPattern = /##([^#]*)##/g; // Pattern to match content that should not be masked + const skipMaskingPattern = SKIP_MASKING_PATTERN; // Pattern to match content that should not be masked const applyPatternMasking = (currentValue: string, pattern: RegExp): string => { let result = ''; @@ -190,6 +192,21 @@ export function maskString( if (!value || value.length === 0) return value; + // try to mask a stringified object correctly + if (value.startsWith('{') && value.endsWith('}')) { + let parsed: unknown; + try { + // if the provided string is nothing else but a stringified object, + // parse it back to object, mask and stringify it again + // ref issue: https://github.com/slippyex/data-guardian/issues/8 + parsed = JSON.parse(value); + const resulting = maskData(parsed); + return JSON.stringify(resulting); + } catch (err) { + // ignore error + } + } + // Check if the content is marked to be unmasked const { isUnmasked, content } = unmaskContent(value); if (isUnmasked) { @@ -199,7 +216,8 @@ export function maskString( if (!types) types = Object.keys(sensitiveContentRegExp) as SensitiveContentKey[]; if (!customSensitiveContentRegExp) customSensitiveContentRegExp = {}; if (options?.excludeMatchers) { - types = types.filter(t => !options.excludeMatchers.includes(t)); + const excludeMatchersSet = new Set(options.excludeMatchers); + types = types.filter(t => !excludeMatchersSet.has(t)); } const applicablePatterns = types.reduce( diff --git a/tests/masking.test.ts b/tests/masking.test.ts index e090508..30528de 100644 --- a/tests/masking.test.ts +++ b/tests/masking.test.ts @@ -431,4 +431,9 @@ describe('Test all possible masking', () => { it('sucks', () => { console.log(maskString('connection to postgres://dbuser:SuperSecretPassword!@localhost:5432/mydb established')); }); + + it('should mask a stringified object correctly', () => { + const data = '{"username":"myuser","password":"something"}'; + expect(maskString(data)).toBe('{"username":"myuser","password":"so*****ng"}'); + }); });