From c0d3e2ee7dab5da3f27284e23703bc396a1e9412 Mon Sep 17 00:00:00 2001 From: Demetrio Girardi Date: Tue, 23 Apr 2024 13:31:43 -0700 Subject: [PATCH 1/2] ortbConverter: fix bug where in nurl is not escaped --- libraries/ortbConverter/processors/banner.js | 4 ++-- src/utils.js | 17 +++++++++++++++-- test/spec/utils_spec.js | 18 +++++++++++++++++- 3 files changed, 34 insertions(+), 5 deletions(-) diff --git a/libraries/ortbConverter/processors/banner.js b/libraries/ortbConverter/processors/banner.js index 51c93b652ef..2d0136c84b2 100644 --- a/libraries/ortbConverter/processors/banner.js +++ b/libraries/ortbConverter/processors/banner.js @@ -1,4 +1,4 @@ -import {createTrackPixelHtml, deepAccess, inIframe, mergeDeep} from '../../../src/utils.js'; +import {createTrackPixelHtml, deepAccess, encodeMacroURI, inIframe, mergeDeep} from '../../../src/utils.js'; import {BANNER} from '../../../src/mediaTypes.js'; import {sizesToFormat} from '../lib/sizes.js'; @@ -24,7 +24,7 @@ export function fillBannerImp(imp, bidRequest, context) { } } -export function bannerResponseProcessor({createPixel = (url) => createTrackPixelHtml(decodeURIComponent(url))} = {}) { +export function bannerResponseProcessor({createPixel = (url) => createTrackPixelHtml(decodeURIComponent(url), encodeMacroURI)} = {}) { return function fillBannerResponse(bidResponse, bid) { if (bidResponse.mediaType === BANNER) { if (bid.adm && bid.nurl) { diff --git a/src/utils.js b/src/utils.js index 71cc78090a6..abb69209020 100644 --- a/src/utils.js +++ b/src/utils.js @@ -489,19 +489,32 @@ export function insertUserSyncIframe(url, done, timeout) { /** * Creates a snippet of HTML that retrieves the specified `url` * @param {string} url URL to be requested + * @param encode * @return {string} HTML snippet that contains the img src = set to `url` */ -export function createTrackPixelHtml(url) { +export function createTrackPixelHtml(url, encode = encodeURI) { if (!url) { return ''; } - let escapedUrl = encodeURI(url); + let escapedUrl = encode(url); let img = '
'; img += '
'; return img; }; +/** + * encodeURI, but preserves macros of the form '${MACRO}' (e.g. '${AUCTION_PRICE}') + * @param url + * @return {string} + */ +export function encodeMacroURI(url) { + const macros = Array.from(url.matchAll(/\$({[^}]+})/g)).map(match => match[1]); + return macros.reduce((str, macro) => { + return str.replace('$' + encodeURIComponent(macro), '$' + macro) + }, encodeURI(url)) +} + /** * Creates a snippet of Iframe HTML that retrieves the specified `url` * @param {string} url plain URL to be requested diff --git a/test/spec/utils_spec.js b/test/spec/utils_spec.js index e0a114d8cf6..8ea68aadecc 100644 --- a/test/spec/utils_spec.js +++ b/test/spec/utils_spec.js @@ -3,7 +3,7 @@ import {expect} from 'chai'; import { TARGETING_KEYS } from 'src/constants.js'; import * as utils from 'src/utils.js'; import {getHighestCpm, getLatestHighestCpmBid, getOldestHighestCpmBid} from '../../src/utils/reducers.js'; -import {binarySearch, deepEqual, memoize, waitForElementToLoad} from 'src/utils.js'; +import {binarySearch, deepEqual, encodeMacroURI, memoize, waitForElementToLoad} from 'src/utils.js'; import {convertCamelToUnderscore} from '../../libraries/appnexusUtils/anUtils.js'; var assert = require('assert'); @@ -778,6 +778,22 @@ describe('Utils', function () { expect(parsed.search).to.equal('?search=test&foo=bar&bar=foo&foo=xxx'); }); }); + + describe('encodeMacroURI', () => { + [ + ['https://www.example.com', 'https://www.example.com'], + ['https://www.example/${MACRO}', 'https://www.example/${MACRO}'], + ['http://www.example/è', `http://www.example/${encodeURIComponent('è')}`], + ['https://www.${MACRO_1}/${MACRO_1}/${MACRO_2}è', 'https://www.${MACRO_1}/${MACRO_1}/${MACRO_2}' + encodeURIComponent('è')], + ['http://${MACRO}${MACRO}/${MACRO}', 'http://${MACRO}${MACRO}/${MACRO}'], + ['{MACRO}${MACRO}', `${encodeURIComponent('{MACRO}')}\${MACRO}`], + ['https://www.example.com?p=${AUCTION_PRICE}', 'https://www.example.com?p=${AUCTION_PRICE}'] + ].forEach(([input, expected]) => { + it(`can encode ${input} -> ${expected}`, () => { + expect(encodeMacroURI(input)).to.eql(expected); + }) + }) + }) }); describe('insertElement', function () { From 3e549aad4a360908c19d465092c2ee6ef04c2985 Mon Sep 17 00:00:00 2001 From: Demetrio Girardi Date: Tue, 23 Apr 2024 13:36:31 -0700 Subject: [PATCH 2/2] fix engageya --- modules/engageyaBidAdapter.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/engageyaBidAdapter.js b/modules/engageyaBidAdapter.js index a66e825e5df..2d2fadfe4ca 100644 --- a/modules/engageyaBidAdapter.js +++ b/modules/engageyaBidAdapter.js @@ -81,7 +81,7 @@ function parseBannerResponse(rec, response) { const title = rec.title && rec.title.trim() ? `` : ''; const displayName = rec.displayName && title ? `` : ''; const trackers = getImpressionTrackers(rec, response) - .map(createTrackPixelHtml) + .map((url) => createTrackPixelHtml(url)) .join(''); return `${style}`; }