Skip to content

Commit

Permalink
IntentIQ Analytics Adapter: browser blacklist (prebid#12119)
Browse files Browse the repository at this point in the history
* add browser blacklist to analytics

* remove log

* fix browser blacklist lowercase

* fix browserblocklist lower case issue, add unit tests

* remove log

* remove unnecessary tests, improve testing logic

* remove code duplication

* export detectbrowser

* description fix

* move detect browser util functions to library

* fix unit test

* update tests

* update tests

* fix test error
  • Loading branch information
eyvazahmadzada authored Sep 3, 2024
1 parent f0b45e8 commit fe19361
Show file tree
Hide file tree
Showing 5 changed files with 121 additions and 77 deletions.
72 changes: 72 additions & 0 deletions libraries/detectBrowserUtils/detectBrowserUtils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import { logError } from '../../src/utils.js';

/**
* Detects the browser using either userAgent or userAgentData
* @return {string} The name of the detected browser or 'unknown' if unable to detect
*/
export function detectBrowser() {
try {
if (navigator.userAgent) {
return detectBrowserFromUserAgent(navigator.userAgent);
} else if (navigator.userAgentData) {
return detectBrowserFromUserAgentData(navigator.userAgentData);
}
} catch (error) {
logError('Error detecting browser:', error);
}
return 'unknown';
}

/**
* Detects the browser from the user agent string
* @param {string} userAgent - The user agent string from the browser
* @return {string} The name of the detected browser or 'unknown' if unable to detect
*/
export function detectBrowserFromUserAgent(userAgent) {
const browserRegexPatterns = {
opera: /Opera|OPR/,
edge: /Edg/,
chrome: /Chrome|CriOS/,
safari: /Safari/,
firefox: /Firefox/,
ie: /MSIE|Trident/,
};

// Check for Chrome first to avoid confusion with Safari
if (browserRegexPatterns.chrome.test(userAgent)) {
return 'chrome';
}

// Now we can safely check for Safari
if (browserRegexPatterns.safari.test(userAgent) && !browserRegexPatterns.chrome.test(userAgent)) {
return 'safari';
}

// Check other browsers
for (const browser in browserRegexPatterns) {
if (browserRegexPatterns[browser].test(userAgent)) {
return browser;
}
}

return 'unknown';
}

/**
* Detects the browser from the NavigatorUAData object
* @param {NavigatorUAData} userAgentData - The user agent data object from the browser
* @return {string} The name of the detected browser or 'unknown' if unable to detect
*/
export function detectBrowserFromUserAgentData(userAgentData) {
const brandNames = userAgentData.brands.map(brand => brand.brand);

if (brandNames.includes('Microsoft Edge')) {
return 'edge';
} else if (brandNames.includes('Opera')) {
return 'opera';
} else if (brandNames.some(brand => brand === 'Chromium' || brand === 'Google Chrome')) {
return 'chrome';
}

return 'unknown';
}
11 changes: 10 additions & 1 deletion modules/intentIqAnalyticsAdapter.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { getStorageManager } from '../src/storageManager.js';
import { config } from '../src/config.js';
import { EVENTS } from '../src/constants.js';
import { MODULE_TYPE_ANALYTICS } from '../src/activities/modules.js';
import { detectBrowser } from '../libraries/detectBrowserUtils/detectBrowserUtils.js';

const MODULE_NAME = 'iiqAnalytics'
const analyticsType = 'endpoint';
Expand Down Expand Up @@ -113,6 +114,8 @@ function initLsValues() {
iiqAnalyticsAnalyticsAdapter.initOptions.partner = iiqArr[0].params.partner;
iiqAnalyticsAnalyticsAdapter.initOptions.currentGroup = iiqAnalyticsAnalyticsAdapter.initOptions.fpid.group;
}

iiqAnalyticsAnalyticsAdapter.initOptions.browserBlackList = typeof iiqArr[0].params.browserBlackList === 'string' ? iiqArr[0].params.browserBlackList.toLowerCase() : '';
}
}

Expand All @@ -134,6 +137,13 @@ function initReadLsIds() {

function bidWon(args) {
if (!iiqAnalyticsAnalyticsAdapter.initOptions.lsValueInitialized) { initLsValues(); }

const currentBrowserLowerCase = detectBrowser();
if (iiqAnalyticsAnalyticsAdapter.initOptions.browserBlackList?.includes(currentBrowserLowerCase)) {
logError('IIQ ANALYTICS -> Browser is in blacklist!');
return;
}

if (iiqAnalyticsAnalyticsAdapter.initOptions.lsValueInitialized && !iiqAnalyticsAnalyticsAdapter.initOptions.lsIdsInitialized) { initReadLsIds(); }
if (!iiqAnalyticsAnalyticsAdapter.initOptions.manualReport) {
ajax(constructFullUrl(preparePayload(args, true)), undefined, null, { method: 'GET' });
Expand Down Expand Up @@ -227,7 +237,6 @@ iiqAnalyticsAnalyticsAdapter.originEnableAnalytics = iiqAnalyticsAnalyticsAdapte
iiqAnalyticsAnalyticsAdapter.enableAnalytics = function (myConfig) {
iiqAnalyticsAnalyticsAdapter.originEnableAnalytics(myConfig); // call the base class function
};

adapterManager.registerAnalyticsAdapter({
adapter: iiqAnalyticsAnalyticsAdapter,
code: MODULE_NAME
Expand Down
76 changes: 1 addition & 75 deletions modules/intentIqIdSystem.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { MODULE_TYPE_UID } from '../src/activities/modules.js';
import { gppDataHandler, uspDataHandler } from '../src/consentHandler.js';
import AES from 'crypto-js/aes.js';
import Utf8 from 'crypto-js/enc-utf8.js';
import { detectBrowser } from '../libraries/detectBrowserUtils/detectBrowserUtils.js';

/**
* @typedef {import('../modules/userId/index.js').Submodule} Submodule
Expand Down Expand Up @@ -172,81 +173,6 @@ export function handleGPPData(data = {}) {
return JSON.stringify(data);
}

/**
* Detects the browser using either userAgent or userAgentData
* @return {string} The name of the detected browser or 'unknown' if unable to detect
*/
function detectBrowser() {
try {
if (navigator.userAgent) {
return detectBrowserFromUserAgent(navigator.userAgent);
} else if (navigator.userAgentData) {
return detectBrowserFromUserAgentData(navigator.userAgentData);
}
} catch (error) {
logError('Error detecting browser:', error);
}
return 'unknown';
}

/**
* Detects the browser from the user agent string
* @param {string} userAgent - The user agent string from the browser
* @return {string} The name of the detected browser or 'unknown' if unable to detect
*/
export function detectBrowserFromUserAgent(userAgent) {
const browserRegexPatterns = {
opera: /Opera|OPR/,
edge: /Edg/,
chrome: /Chrome|CriOS/,
safari: /Safari/,
firefox: /Firefox/,
ie: /MSIE|Trident/,
};

// Check for Chrome first to avoid confusion with Safari
if (browserRegexPatterns.chrome.test(userAgent)) {
return 'chrome';
}

// Now we can safely check for Safari
if (browserRegexPatterns.safari.test(userAgent) && !browserRegexPatterns.chrome.test(userAgent)) {
return 'safari';
}

// Check other browsers
for (const browser in browserRegexPatterns) {
if (browserRegexPatterns[browser].test(userAgent)) {
return browser;
}
}

return 'unknown';
}
/**
* @typedef {Object} NavigatorUAData
* @property {Array<{brand: string, version: string}>} brands - The list of brands associated with the user agent.
*/

/**
* Detects the browser from the NavigatorUAData object
* @param {NavigatorUAData} userAgentData - The user agent data object from the browser
* @return {string} The name of the detected browser or 'unknown' if unable to detect
*/
export function detectBrowserFromUserAgentData(userAgentData) {
const brandNames = userAgentData.brands.map(brand => brand.brand);

if (brandNames.includes('Microsoft Edge')) {
return 'edge';
} else if (brandNames.includes('Opera')) {
return 'opera';
} else if (brandNames.some(brand => brand === 'Chromium' || brand === 'Google Chrome')) {
return 'chrome';
}

return 'unknown';
}

/**
* Processes raw client hints data into a structured format.
* @param {object} clientHints - Raw client hints data
Expand Down
36 changes: 36 additions & 0 deletions test/spec/modules/intentIqAnalyticsAdapter_spec.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { expect } from 'chai';
import iiqAnalyticsAnalyticsAdapter from 'modules/intentIqAnalyticsAdapter.js';
import * as utils from 'src/utils.js';
import * as detectBrowserUtils from '../../../libraries/detectBrowserUtils/detectBrowserUtils';
import { server } from 'test/mocks/xhr.js';
import { config } from 'src/config.js';
import { EVENTS } from 'src/constants.js';
Expand Down Expand Up @@ -67,6 +68,7 @@ let wonRequest = {

describe('IntentIQ tests all', function () {
let logErrorStub;
let detectBrowserStub;

beforeEach(function () {
logErrorStub = sinon.stub(utils, 'logError');
Expand Down Expand Up @@ -94,6 +96,7 @@ describe('IntentIQ tests all', function () {

afterEach(function () {
logErrorStub.restore();
if (detectBrowserStub) detectBrowserStub.restore();
config.getConfig.restore();
events.getEvents.restore();
iiqAnalyticsAnalyticsAdapter.disableAnalytics();
Expand Down Expand Up @@ -172,4 +175,37 @@ describe('IntentIQ tests all', function () {
expect(iiqAnalyticsAnalyticsAdapter.initOptions.currentGroup).to.equal('B');
expect(iiqAnalyticsAnalyticsAdapter.initOptions.fpid).to.be.not.null;
});

it('should not send request if the browser is in blacklist (chrome)', function () {
const USERID_CONFIG_BROWSER = [...USERID_CONFIG];
USERID_CONFIG_BROWSER[0].params.browserBlackList = 'ChrOmE';

config.getConfig.restore();
sinon.stub(config, 'getConfig').withArgs('userSync.userIds').returns(USERID_CONFIG_BROWSER);
detectBrowserStub = sinon.stub(detectBrowserUtils, 'detectBrowser').returns('chrome');

localStorage.setItem(FIRST_PARTY_KEY, defaultData);
events.emit(EVENTS.BID_WON, wonRequest);

expect(server.requests.length).to.equal(0);
});

it('should send request if the browser is not in blacklist (safari)', function () {
const USERID_CONFIG_BROWSER = [...USERID_CONFIG];
USERID_CONFIG_BROWSER[0].params.browserBlackList = 'chrome,firefox';

config.getConfig.restore();
sinon.stub(config, 'getConfig').withArgs('userSync.userIds').returns(USERID_CONFIG_BROWSER);
detectBrowserStub = sinon.stub(detectBrowserUtils, 'detectBrowser').returns('safari');

localStorage.setItem(FIRST_PARTY_KEY, defaultData);
events.emit(EVENTS.BID_WON, wonRequest);

expect(server.requests.length).to.be.above(0);
const request = server.requests[0];
expect(request.url).to.contain(`https://reports.intentiq.com/report?pid=${partner}&mct=1`);
expect(request.url).to.contain(`&jsver=${version}&vrref=http://localhost:9876/`);
expect(request.url).to.contain('&payload=');
expect(request.url).to.contain('iiqid=f961ffb1-a0e1-4696-a9d2-a21d815bd344');
});
});
3 changes: 2 additions & 1 deletion test/spec/modules/intentIqIdSystem_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@ import { expect } from 'chai';
import { intentIqIdSubmodule, storage } from 'modules/intentIqIdSystem.js';
import * as utils from 'src/utils.js';
import { server } from 'test/mocks/xhr.js';
import { CLIENT_HINTS_KEY, FIRST_PARTY_KEY, decryptData, detectBrowserFromUserAgent, detectBrowserFromUserAgentData, handleClientHints, handleGPPData, readData } from '../../../modules/intentIqIdSystem';
import { CLIENT_HINTS_KEY, FIRST_PARTY_KEY, decryptData, handleClientHints, handleGPPData, readData } from '../../../modules/intentIqIdSystem';
import { gppDataHandler, uspDataHandler } from '../../../src/consentHandler';
import { clearAllCookies } from '../../helpers/cookies';
import { detectBrowserFromUserAgent, detectBrowserFromUserAgentData } from '../../../libraries/detectBrowserUtils/detectBrowserUtils';

const partner = 10;
const pai = '11';
Expand Down

0 comments on commit fe19361

Please sign in to comment.