Skip to content

Commit 74a1ffc

Browse files
authored
ID5 Adapter: protect against local storage writing without consent (#9587)
* id-6129: don't write to local storage without consent * id-6129: clean up * id-6129: clean up * id-6129: refactor * id-6129: use deepAccess * id-6129: unit tests * id-6129: logging * id-6129: improve log
1 parent 18db442 commit 74a1ffc

File tree

2 files changed

+72
-2
lines changed

2 files changed

+72
-2
lines changed

modules/id5IdSystem.js

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,11 @@ export const id5IdSubmodule = {
113113
return undefined;
114114
}
115115

116+
if (!hasWriteConsentToLocalStorage(consentData)) {
117+
logInfo(LOG_PREFIX + 'Skipping ID5 local storage write because no consent given.')
118+
return undefined;
119+
}
120+
116121
const resp = function (cbFunction) {
117122
new IdFetchFlow(submoduleConfig, consentData, cacheIdObj, uspDataHandler.getConsentData()).execute()
118123
.then(response => {
@@ -140,6 +145,11 @@ export const id5IdSubmodule = {
140145
extendId(config, consentData, cacheIdObj) {
141146
hasRequiredConfig(config);
142147

148+
if (!hasWriteConsentToLocalStorage(consentData)) {
149+
logInfo(LOG_PREFIX + 'No consent given for ID5 local storage writing, skipping nb increment.')
150+
return cacheIdObj;
151+
}
152+
143153
const partnerId = (config && config.params && config.params.partner) || 0;
144154
incrementNb(partnerId);
145155

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

389+
/**
390+
* Check to see if we can write to local storage based on purpose consent 1, and that we have vendor consent (ID5=131)
391+
* @param {ConsentData} consentData
392+
* @returns {boolean}
393+
*/
394+
function hasWriteConsentToLocalStorage(consentData) {
395+
const hasGdpr = consentData && typeof consentData.gdprApplies === 'boolean' && consentData.gdprApplies;
396+
const localstorageConsent = deepAccess(consentData, `vendorData.purpose.consents.1`)
397+
const id5VendorConsent = deepAccess(consentData, `vendorData.vendor.consents.${GVLID.toString()}`)
398+
if (hasGdpr && (!localstorageConsent || !id5VendorConsent)) {
399+
return false;
400+
}
401+
return true;
402+
}
403+
379404
submodule('userId', id5IdSubmodule);

test/spec/modules/id5IdSystem_spec.js

Lines changed: 47 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,18 @@ describe('ID5 ID System', function () {
5252
'signature': ID5_RESPONSE_SIGNATURE,
5353
'link_type': ID5_RESPONSE_LINK_TYPE
5454
};
55+
const ALLOWED_ID5_VENDOR_DATA = {
56+
purpose: {
57+
consents: {
58+
1: true
59+
}
60+
},
61+
vendor: {
62+
consents: {
63+
131: true
64+
}
65+
}
66+
}
5567

5668
function getId5FetchConfig(storageName = ID5_STORAGE_NAME, storageType = 'html5') {
5769
return {
@@ -205,6 +217,37 @@ describe('ID5 ID System', function () {
205217
});
206218
});
207219

220+
describe('Check for valid consent', function() {
221+
const dataConsentVals = [
222+
[{purpose: {consents: {1: false}}}, {vendor: {consents: {131: true}}}, ' no purpose consent'],
223+
[{purpose: {consents: {1: true}}}, {vendor: {consents: {131: false}}}, ' no vendor consent'],
224+
[{purpose: {consents: {1: false}}}, {vendor: {consents: {131: false}}}, ' no purpose and vendor consent'],
225+
[{purpose: {consents: undefined}}, {vendor: {consents: {131: true}}}, ' undefined purpose consent'],
226+
[{purpose: {consents: {1: false}}}, {vendor: {consents: undefined}}], ' undefined vendor consent',
227+
[undefined, {vendor: {consents: {131: true}}}, ' undefined purpose'],
228+
[{purpose: {consents: {1: true}}}, {vendor: undefined}, ' undefined vendor'],
229+
[{purpose: {consents: {1: true}}}, {vendor: {consents: {31: true}}}, ' incorrect vendor consent']
230+
];
231+
232+
dataConsentVals.forEach(function([purposeConsent, vendorConsent, caseName]) {
233+
it('should fail with invalid consent because of ' + caseName, function() {
234+
let dataConsent = {
235+
gdprApplies: true,
236+
consentString: 'consentString',
237+
vendorData: {
238+
purposeConsent, vendorConsent
239+
}
240+
}
241+
expect(id5IdSubmodule.getId(config)).is.eq(undefined);
242+
expect(id5IdSubmodule.getId(config, dataConsent)).is.eq(undefined);
243+
244+
let cacheIdObject = 'cacheIdObject';
245+
expect(id5IdSubmodule.extendId(config)).is.eq(undefined);
246+
expect(id5IdSubmodule.extendId(config, dataConsent, cacheIdObject)).is.eq(cacheIdObject);
247+
});
248+
});
249+
});
250+
208251
describe('Xhr Requests from getId()', function () {
209252
const responseHeader = {'Content-Type': 'application/json'};
210253

@@ -248,7 +291,8 @@ describe('ID5 ID System', function () {
248291
let xhrServerMock = new XhrServerMock(sinon.createFakeServer())
249292
let consentData = {
250293
gdprApplies: true,
251-
consentString: 'consentString'
294+
consentString: 'consentString',
295+
vendorData: ALLOWED_ID5_VENDOR_DATA
252296
}
253297

254298
let submoduleResponse = callSubmoduleGetId(getId5FetchConfig(), consentData, undefined);
@@ -297,7 +341,8 @@ describe('ID5 ID System', function () {
297341
let xhrServerMock = new XhrServerMock(sinon.createFakeServer())
298342
let consentData = {
299343
gdprApplies: true,
300-
consentString: 'consentString'
344+
consentString: 'consentString',
345+
vendorData: ALLOWED_ID5_VENDOR_DATA
301346
}
302347

303348
let submoduleResponse = callSubmoduleGetId(getId5FetchConfig(), consentData, undefined);

0 commit comments

Comments
 (0)