From 788f70bdd1c2d64e4eb96dc6f99e0720cff80882 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20Wr=C3=B3blewski?= Date: Mon, 6 May 2024 17:54:10 +0200 Subject: [PATCH] Add variable for logging decoded content Improve checkIfValidBase64 and add more tests --- .../trusted-replace-outbound-text.ts | 17 +++++++++---- .../trusted-replace-outbound-text.test.js | 25 ++++++++++++++----- 2 files changed, 31 insertions(+), 11 deletions(-) diff --git a/src/scriptlets/trusted-replace-outbound-text.ts b/src/scriptlets/trusted-replace-outbound-text.ts index e2a1f39a7..564cdc025 100644 --- a/src/scriptlets/trusted-replace-outbound-text.ts +++ b/src/scriptlets/trusted-replace-outbound-text.ts @@ -153,9 +153,17 @@ export function trustedReplaceOutboundText( * @param str - The string to be checked. * @returns A boolean indicating whether the string is a valid base64 encoded string. */ - const checkIfValidBase64 = (str: string) => { + const checkIfValidBase64 = (str: string): boolean => { try { - return str === '' ? false : btoa(atob(str)) === str; + if (str === '') { + return false; + } + const decodedString = atob(str); + const encodedString = btoa(decodedString); + // Encoded string may contains padding characters, so it's necessary to remove it before comparison + const stringWithoutPadding = str.replace(/=+$/, ''); + const encodedStringWithoutPadding = encodedString.replace(/=+$/, ''); + return encodedStringWithoutPadding === stringWithoutPadding; } catch (e) { return false; } @@ -216,6 +224,7 @@ export function trustedReplaceOutboundText( const logOriginalContent = !textToReplace || !!logContent; const logModifiedContent = !!logContent; + const logDecodedContent = !!decodeMethod && !!logContent; // This flag allows to prevent infinite loops when trapping props that are used by scriptlet's own code. let isMatchingSuspended = false; @@ -242,9 +251,7 @@ export function trustedReplaceOutboundText( } const patternRegexp = toRegExp(textToReplace); - // If textToReplace is not set and decodeMethod and logContent are set - // then decoded content should be logged. - const modifiedContent = textToReplace || (decodeMethod && logContent) + const modifiedContent = textToReplace || logDecodedContent ? decodeAndReplaceContent(result, patternRegexp, replacement, decodeMethod, logContent) : result; diff --git a/tests/scriptlets/trusted-replace-outbound-text.test.js b/tests/scriptlets/trusted-replace-outbound-text.test.js index 541d302cd..ebb8b40ba 100644 --- a/tests/scriptlets/trusted-replace-outbound-text.test.js +++ b/tests/scriptlets/trusted-replace-outbound-text.test.js @@ -179,6 +179,19 @@ test('replace text - Array.prototype.join, decode base64', (assert) => { assert.strictEqual(window.hit, 'FIRED', 'hit function fired'); }); +test('replace text - Array.prototype.join, decode base64, test for padding "="', (assert) => { + runScriptlet(name, ['Array.prototype.join', 'fooBarTest=', 'Test=', 'base64']); + const expectedText = 'Test='; + const expectedTextInBase64 = btoa(expectedText); + const textInBase64 = 'Zm9vQmFyVGVzdD0'; + const splittedText = textInBase64.split(''); + const result = splittedText.join(''); + const decodedResult = atob(result); + assert.deepEqual(result, expectedTextInBase64, 'content in base64 is modified'); + assert.deepEqual(decodedResult, expectedText, 'content is modified'); + assert.strictEqual(window.hit, 'FIRED', 'hit function fired'); +}); + test('replace text decode - Array.prototype.join, match stack', (assert) => { runScriptlet(name, ['Array.prototype.join', 'disable_ads:false', 'disable_ads:true', 'base64', 'decodeStackFunc']); const decodeStackFunc = () => { @@ -203,12 +216,12 @@ test('log - Array.prototype.join, base64', (assert) => { const argsLength = args.length; const consoleContent = args[0]; if (argsLength === 1 && consoleContent.includes('Original text content:')) { - assert.ok(consoleContent.includes('Original text content:'), 'should log original text in console'); - assert.ok(consoleContent.includes('Zm9vOmJhcixxd2VydHk6MTIzNA=='), 'should log original text in console'); + assert.ok(consoleContent.includes('Original text content:'), '1 should log original text in console'); + assert.ok(consoleContent.includes('Zm9vOmJhcixxd2VydHk6MTIzNA=='), '2 should log original text in console'); } if (argsLength === 1 && consoleContent.includes('Decoded text content:')) { - assert.ok(consoleContent.includes('Decoded text content:'), 'log information about decoded content'); - assert.ok(consoleContent.includes('foo:bar,qwerty:1234'), 'log information about decoded content'); + assert.ok(consoleContent.includes('Decoded text content:'), '1 log information about decoded content'); + assert.ok(consoleContent.includes('foo:bar,qwerty:1234'), '2 log information about decoded content'); } if (argsLength === 1 && consoleContent.includes('Decoded text content was not modified')) { assert.ok( @@ -227,7 +240,7 @@ test('log - Array.prototype.join, base64', (assert) => { const splittedText = textInBase64.split(''); const result = splittedText.join(''); const decodedResult = atob(result); - assert.deepEqual(result, expectedTextInBase64, 'Text content is intact'); - assert.deepEqual(decodedResult, expectedText, 'Text content is intact'); + assert.deepEqual(result, expectedTextInBase64, 'Encoded text content is intact'); + assert.deepEqual(decodedResult, expectedText, 'Decoded text content is intact'); assert.strictEqual(window.hit, 'FIRED', 'hit function fired'); });