diff --git a/src/spans/awsSpan.test.js b/src/spans/awsSpan.test.js index 054fb17c..8a667498 100644 --- a/src/spans/awsSpan.test.js +++ b/src/spans/awsSpan.test.js @@ -444,6 +444,36 @@ describe('awsSpan', () => { expect(endSpan.envs.length).toBeGreaterThan(startSpan.envs.length); }); + test('getEndFunctionSpan does not modify returnValue payload', () => { + const functionSpan = { + id: '6d26e3c8-60a6-4cee-8a70-f525f47a4caf_started', + }; + + const handlerReturnValue = { + err: null, + data: { + string: 'value', + object: { + string: 'value', + object: { + string: 'value', + }, + }, + }, + }; + process.env[LUMIGO_SECRET_MASKING_EXACT_PATH] = + '["string","object.string", "object.object.string"]'; + + const endFunctionSpan = awsSpan.getEndFunctionSpan(functionSpan, handlerReturnValue); + expect(endFunctionSpan.return_value).toEqual( + '{"string":"****","object":{"string":"****","object":{"string":"****"}}}' + ); + // here we expect the original data to be untouched + expect(handlerReturnValue.data.string).toEqual('value'); + expect(handlerReturnValue.data.object.string).toEqual('value'); + expect(handlerReturnValue.data.object.object.string).toEqual('value'); + }); + test('Lambda invoked by S3 -> shouldnt scrub known S3 fields', () => { const { context } = TracerGlobals.getHandlerInputs(); const event = { diff --git a/src/utils/payloadStringify.js b/src/utils/payloadStringify.js index 2fe5bc67..c4aaeba2 100644 --- a/src/utils/payloadStringify.js +++ b/src/utils/payloadStringify.js @@ -26,6 +26,7 @@ const nativeTypes = ['string', 'bigint', 'number', 'undefined', 'boolean']; const SCRUBBED_TEXT = '****'; const TRUNCATED_TEXT = '...[too long]'; const FAILED_SCRUBBING_BY_PATH = 'Failed to scrub payload by exact path'; +const clone = require('rfdc')(); const isNativeType = (obj) => nativeTypes.includes(typeof obj); @@ -176,6 +177,30 @@ function logSecretMaskingDebug(logger, message, additionalData) { } } +function scrubPayloadBasedOnExactPath(originalPayload) { + let payload = clone(originalPayload); + let secretPaths = getSecretPaths(); + if (secretPaths.length > 0) { + const uniquePaths = getUniqPaths(secretPaths); + if (isString(payload)) { + payload = safeExecute( + scrubJsonStringBySecretPath, + FAILED_SCRUBBING_BY_PATH, + logger.LOG_LEVELS.DEBUG, + payload + )(payload, secretPaths, uniquePaths, ''); + } else { + payload = safeExecute( + scrubJsonBySecretPath, + FAILED_SCRUBBING_BY_PATH, + logger.LOG_LEVELS.DEBUG, + payload + )(payload, secretPaths, uniquePaths, ''); + } + } + return payload; +} + export const payloadStringify = ( payload, maxPayloadSize = getEventEntitySize(), @@ -190,29 +215,9 @@ export const payloadStringify = ( let isPruned = false; - if (getSecretMaskingExactPath()) { - let secretPaths = getSecretPaths(); - if (secretPaths.length > 0) { - const uniquePaths = getUniqPaths(secretPaths); - if (isString(payload)) { - payload = safeExecute( - scrubJsonStringBySecretPath, - FAILED_SCRUBBING_BY_PATH, - logger.LOG_LEVELS.DEBUG, - payload - )(payload, secretPaths, uniquePaths, ''); - } else { - payload = safeExecute( - scrubJsonBySecretPath, - FAILED_SCRUBBING_BY_PATH, - logger.LOG_LEVELS.DEBUG, - payload - )(payload, secretPaths, uniquePaths, ''); - } - } - } + let result = getSecretMaskingExactPath() ? scrubPayloadBasedOnExactPath(payload) : payload; - let result = JSON.stringify(payload, function (key, value) { + result = JSON.stringify(result, function (key, value) { const type = typeof value; const isObj = type === 'object'; const isStr = type === 'string';