Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ID5 Adapter: protect against local storage writing without consent #9587

Merged
25 changes: 25 additions & 0 deletions modules/id5IdSystem.js
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,11 @@ export const id5IdSubmodule = {
return undefined;
}

if (!hasWriteConsentToLocalStorage(consentData)) {
logInfo(LOG_PREFIX + 'Skipping ID5 local storage write because no consent given.')
return undefined;
}

const resp = function (cbFunction) {
new IdFetchFlow(submoduleConfig, consentData, cacheIdObj, uspDataHandler.getConsentData()).execute()
.then(response => {
Expand Down Expand Up @@ -140,6 +145,11 @@ export const id5IdSubmodule = {
extendId(config, consentData, cacheIdObj) {
hasRequiredConfig(config);

if (!hasWriteConsentToLocalStorage(consentData)) {
logInfo(LOG_PREFIX + 'No consent given for ID5 local storage writing, skipping nb increment.')
return cacheIdObj;
}

const partnerId = (config && config.params && config.params.partner) || 0;
incrementNb(partnerId);

Expand Down Expand Up @@ -376,4 +386,19 @@ export function storeInLocalStorage(key, value, expDays) {
storage.setDataInLocalStorage(`${key}`, value);
}

/**
* Check to see if we can write to local storage based on purpose consent 1, and that we have vendor consent (ID5=131)
* @param {ConsentData} consentData
* @returns {boolean}
*/
function hasWriteConsentToLocalStorage(consentData) {
const hasGdpr = consentData && typeof consentData.gdprApplies === 'boolean' && consentData.gdprApplies;
const localstorageConsent = deepAccess(consentData, `vendorData.purpose.consents.1`)
const id5VendorConsent = deepAccess(consentData, `vendorData.vendor.consents.${GVLID.toString()}`)
if (hasGdpr && (!localstorageConsent || !id5VendorConsent)) {
return false;
}
return true;
}

submodule('userId', id5IdSubmodule);
49 changes: 47 additions & 2 deletions test/spec/modules/id5IdSystem_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,18 @@ describe('ID5 ID System', function () {
'signature': ID5_RESPONSE_SIGNATURE,
'link_type': ID5_RESPONSE_LINK_TYPE
};
const ALLOWED_ID5_VENDOR_DATA = {
purpose: {
consents: {
1: true
}
},
vendor: {
consents: {
131: true
}
}
}

function getId5FetchConfig(storageName = ID5_STORAGE_NAME, storageType = 'html5') {
return {
Expand Down Expand Up @@ -205,6 +217,37 @@ describe('ID5 ID System', function () {
});
});

describe('Check for valid consent', function() {
const dataConsentVals = [
[{purpose: {consents: {1: false}}}, {vendor: {consents: {131: true}}}, ' no purpose consent'],
[{purpose: {consents: {1: true}}}, {vendor: {consents: {131: false}}}, ' no vendor consent'],
[{purpose: {consents: {1: false}}}, {vendor: {consents: {131: false}}}, ' no purpose and vendor consent'],
[{purpose: {consents: undefined}}, {vendor: {consents: {131: true}}}, ' undefined purpose consent'],
[{purpose: {consents: {1: false}}}, {vendor: {consents: undefined}}], ' undefined vendor consent',
[undefined, {vendor: {consents: {131: true}}}, ' undefined purpose'],
[{purpose: {consents: {1: true}}}, {vendor: undefined}, ' undefined vendor'],
[{purpose: {consents: {1: true}}}, {vendor: {consents: {31: true}}}, ' incorrect vendor consent']
];

dataConsentVals.forEach(function([purposeConsent, vendorConsent, caseName]) {
it('should fail with invalid consent because of ' + caseName, function() {
let dataConsent = {
gdprApplies: true,
consentString: 'consentString',
vendorData: {
purposeConsent, vendorConsent
}
}
expect(id5IdSubmodule.getId(config)).is.eq(undefined);
expect(id5IdSubmodule.getId(config, dataConsent)).is.eq(undefined);

let cacheIdObject = 'cacheIdObject';
expect(id5IdSubmodule.extendId(config)).is.eq(undefined);
expect(id5IdSubmodule.extendId(config, dataConsent, cacheIdObject)).is.eq(cacheIdObject);
});
});
});

describe('Xhr Requests from getId()', function () {
const responseHeader = {'Content-Type': 'application/json'};

Expand Down Expand Up @@ -248,7 +291,8 @@ describe('ID5 ID System', function () {
let xhrServerMock = new XhrServerMock(sinon.createFakeServer())
let consentData = {
gdprApplies: true,
consentString: 'consentString'
consentString: 'consentString',
vendorData: ALLOWED_ID5_VENDOR_DATA
}

let submoduleResponse = callSubmoduleGetId(getId5FetchConfig(), consentData, undefined);
Expand Down Expand Up @@ -297,7 +341,8 @@ describe('ID5 ID System', function () {
let xhrServerMock = new XhrServerMock(sinon.createFakeServer())
let consentData = {
gdprApplies: true,
consentString: 'consentString'
consentString: 'consentString',
vendorData: ALLOWED_ID5_VENDOR_DATA
}

let submoduleResponse = callSubmoduleGetId(getId5FetchConfig(), consentData, undefined);
Expand Down