prebid.js 7.51.0 → 7.52.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/33acrossBidAdapter.js +1 -1
- package/dist/33acrossIdSystem.js +1 -1
- package/dist/adagioBidAdapter.js +1 -1
- package/dist/adbookpspBidAdapter.js +1 -1
- package/dist/adgenerationBidAdapter.js +1 -1
- package/dist/adqueryBidAdapter.js +1 -1
- package/dist/adrelevantisBidAdapter.js +1 -1
- package/dist/adriverIdSystem.js +1 -1
- package/dist/adtrgtmeBidAdapter.js +1 -1
- package/dist/adxcgBidAdapter.js +1 -1
- package/dist/adyoulikeBidAdapter.js +1 -1
- package/dist/ajaBidAdapter.js +1 -1
- package/dist/allowActivities.js +1 -0
- package/dist/amxBidAdapter.js +1 -1
- package/dist/amxIdSystem.js +1 -1
- package/dist/appierAnalyticsAdapter.js +1 -1
- package/dist/appnexusBidAdapter.js +1 -1
- package/dist/asoBidAdapter.js +1 -1
- package/dist/axonixBidAdapter.js +1 -1
- package/dist/bidglassBidAdapter.js +1 -1
- package/dist/big-richmediaBidAdapter.js +1 -1
- package/dist/bridgewellBidAdapter.js +1 -1
- package/dist/brightMountainMediaBidAdapter.js +1 -1
- package/dist/carodaBidAdapter.js +1 -1
- package/dist/chtnwBidAdapter.js +1 -1
- package/dist/concertBidAdapter.js +1 -1
- package/dist/connectIdSystem.js +1 -1
- package/dist/connectadBidAdapter.js +1 -1
- package/dist/consumableBidAdapter.js +1 -1
- package/dist/conversantAnalyticsAdapter.js +1 -1
- package/dist/conversantBidAdapter.js +1 -1
- package/dist/craftBidAdapter.js +1 -1
- package/dist/criteoBidAdapter.js +1 -1
- package/dist/cwireBidAdapter.js +1 -1
- package/dist/dependencies.json +6 -0
- package/dist/dspxBidAdapter.js +1 -1
- package/dist/eplanningBidAdapter.js +1 -1
- package/dist/feedadBidAdapter.js +1 -1
- package/dist/finativeBidAdapter.js +1 -1
- package/dist/freewheel-sspBidAdapter.js +1 -1
- package/dist/gdprEnforcement.js +1 -1
- package/dist/glimpseBidAdapter.js +1 -1
- package/dist/gmosspBidAdapter.js +1 -1
- package/dist/goldbachBidAdapter.js +1 -1
- package/dist/greenbidsAnalyticsAdapter.js +1 -1
- package/dist/greenbidsRtdProvider.js +1 -1
- package/dist/gridBidAdapter.js +1 -1
- package/dist/growthCodeRtdProvider.js +1 -0
- package/dist/gumgumBidAdapter.js +1 -1
- package/dist/h12mediaBidAdapter.js +1 -1
- package/dist/id5IdSystem.js +1 -1
- package/dist/improvedigitalBidAdapter.js +1 -1
- package/dist/inmarBidAdapter.js +1 -1
- package/dist/insticatorBidAdapter.js +1 -1
- package/dist/ixBidAdapter.js +1 -1
- package/dist/justpremiumBidAdapter.js +1 -1
- package/dist/kargoBidAdapter.js +1 -1
- package/dist/konduitAnalyticsAdapter.js +1 -1
- package/dist/kueezBidAdapter.js +1 -1
- package/dist/kueezRtbBidAdapter.js +1 -1
- package/dist/kulturemediaBidAdapter.js +1 -1
- package/dist/lassoBidAdapter.js +1 -1
- package/dist/lifestreetBidAdapter.js +1 -1
- package/dist/liveyieldAnalyticsAdapter.js +1 -1
- package/dist/logicadBidAdapter.js +1 -1
- package/dist/loglyliftBidAdapter.js +1 -1
- package/dist/magniteAnalyticsAdapter.js +1 -1
- package/dist/malltvAnalyticsAdapter.js +1 -1
- package/dist/marsmediaBidAdapter.js +1 -1
- package/dist/mediafuseBidAdapter.js +1 -1
- package/dist/mediasquareBidAdapter.js +1 -1
- package/dist/mgidBidAdapter.js +1 -1
- package/dist/minutemediaBidAdapter.js +1 -1
- package/dist/minutemediaplusBidAdapter.js +1 -1
- package/dist/nexx360BidAdapter.js +1 -1
- package/dist/not-for-prod/prebid.js +136 -132
- package/dist/objectGuard.js +1 -0
- package/dist/oguryBidAdapter.js +1 -1
- package/dist/onetagBidAdapter.js +1 -1
- package/dist/ooloAnalyticsAdapter.js +1 -1
- package/dist/optidigitalBidAdapter.js +1 -1
- package/dist/outbrainBidAdapter.js +1 -1
- package/dist/oxxionAnalyticsAdapter.js +1 -0
- package/dist/pairIdSystem.js +1 -1
- package/dist/parrableIdSystem.js +1 -1
- package/dist/pixfutureBidAdapter.js +1 -1
- package/dist/prebid-core.js +1 -1
- package/dist/publinkIdSystem.js +1 -1
- package/dist/pubmaticBidAdapter.js +1 -1
- package/dist/pubwiseAnalyticsAdapter.js +1 -1
- package/dist/pxyzBidAdapter.js +1 -1
- package/dist/quantcastBidAdapter.js +1 -1
- package/dist/readpeakBidAdapter.js +1 -1
- package/dist/relaidoBidAdapter.js +1 -1
- package/dist/retailspotBidAdapter.js +1 -1
- package/dist/rhythmoneBidAdapter.js +1 -1
- package/dist/riseBidAdapter.js +1 -1
- package/dist/rtdModule.js +1 -1
- package/dist/rubiconAnalyticsAdapter.js +1 -1
- package/dist/rubiconBidAdapter.js +1 -1
- package/dist/seedingAllianceBidAdapter.js +1 -1
- package/dist/seedtagBidAdapter.js +1 -1
- package/dist/sharethroughAnalyticsAdapter.js +1 -1
- package/dist/sharethroughBidAdapter.js +1 -1
- package/dist/shinezBidAdapter.js +1 -1
- package/dist/smaatoBidAdapter.js +1 -1
- package/dist/smartadserverBidAdapter.js +1 -1
- package/dist/smartxBidAdapter.js +1 -1
- package/dist/smilewantedBidAdapter.js +1 -1
- package/dist/sonobiBidAdapter.js +1 -1
- package/dist/sovrnAnalyticsAdapter.js +1 -1
- package/dist/sovrnBidAdapter.js +1 -1
- package/dist/sspBCBidAdapter.js +1 -1
- package/dist/stvBidAdapter.js +1 -1
- package/dist/sublimeBidAdapter.js +1 -1
- package/dist/synacormediaBidAdapter.js +1 -1
- package/dist/targetVideoBidAdapter.js +1 -1
- package/dist/teadsBidAdapter.js +1 -1
- package/dist/trionBidAdapter.js +1 -1
- package/dist/tripleliftBidAdapter.js +1 -1
- package/dist/ttdBidAdapter.js +1 -1
- package/dist/ucfunnelAnalyticsAdapter.js +1 -1
- package/dist/uid2IdSystem.js +1 -1
- package/dist/underdogmediaBidAdapter.js +1 -1
- package/dist/undertoneBidAdapter.js +1 -1
- package/dist/userId.js +1 -1
- package/dist/vidazooBidAdapter.js +1 -1
- package/dist/videobyteBidAdapter.js +1 -1
- package/dist/visxBidAdapter.js +1 -1
- package/dist/vuukleBidAdapter.js +1 -1
- package/dist/widespaceBidAdapter.js +1 -1
- package/dist/winrBidAdapter.js +1 -1
- package/dist/yahoosspBidAdapter.js +1 -1
- package/dist/yieldmoBidAdapter.js +1 -1
- package/dist/yieldoneAnalyticsAdapter.js +1 -1
- package/dist/zeta_global_sspBidAdapter.js +1 -1
- package/integrationExamples/gpt/growthcode.html +20 -9
- package/libraries/objectGuard/objectGuard.js +108 -0
- package/libraries/objectGuard/ortbGuard.js +88 -0
- package/modules/adgenerationBidAdapter.js +14 -5
- package/modules/adriverIdSystem.js +1 -1
- package/modules/allowActivities.js +74 -0
- package/modules/connectIdSystem.js +89 -13
- package/modules/connectIdSystem.md +4 -7
- package/modules/criteoBidAdapter.js +9 -0
- package/modules/gdprEnforcement.js +98 -169
- package/modules/growthCodeRtdProvider.js +131 -0
- package/modules/growthCodeRtdProvider.md +55 -0
- package/modules/ixBidAdapter.js +5 -2
- package/modules/oxxionAnalyticsAdapter.js +212 -0
- package/modules/oxxionAnalyticsAdapter.md +33 -0
- package/modules/pairIdSystem.js +6 -6
- package/modules/rtdModule/index.js +12 -1
- package/modules/sharethroughBidAdapter.js +2 -2
- package/modules/smartadserverBidAdapter.js +5 -0
- package/modules/stvBidAdapter.js +34 -1
- package/modules/undertoneBidAdapter.js +9 -1
- package/modules/userId/index.js +69 -41
- package/modules/yahoosspBidAdapter.js +45 -3
- package/modules/yahoosspBidAdapter.md +1 -1
- package/modules/zeta_global_sspBidAdapter.js +1 -0
- package/package.json +1 -1
- package/src/activities/activities.js +47 -0
- package/src/activities/activityParams.js +8 -0
- package/src/activities/modules.js +1 -1
- package/src/activities/params.js +59 -0
- package/src/activities/redactor.js +157 -0
- package/src/activities/rules.js +95 -0
- package/src/adapterManager.js +45 -8
- package/src/fpd/rootDomain.js +1 -1
- package/src/prebid.js +1 -1
- package/src/storageManager.js +57 -44
- package/src/userSync.js +35 -18
- package/test/spec/activities/allowActivites_spec.js +138 -0
- package/test/spec/activities/objectGuard_spec.js +144 -0
- package/test/spec/activities/ortbGuard_spec.js +140 -0
- package/test/spec/activities/params_spec.js +25 -0
- package/test/spec/activities/redactor_spec.js +296 -0
- package/test/spec/activities/rules_spec.js +135 -0
- package/test/spec/modules/adgenerationBidAdapter_spec.js +52 -12
- package/test/spec/modules/connectIdSystem_spec.js +291 -23
- package/test/spec/modules/criteoBidAdapter_spec.js +84 -0
- package/test/spec/modules/gdprEnforcement_spec.js +127 -414
- package/test/spec/modules/growthCodeRtdProvider_spec.js +127 -0
- package/test/spec/modules/ixBidAdapter_spec.js +2 -1
- package/test/spec/modules/oxxionAnalyticsAdapter_spec.js +324 -0
- package/test/spec/modules/pairIdSystem_spec.js +16 -3
- package/test/spec/modules/realTimeDataModule_spec.js +1 -1
- package/test/spec/modules/sharethroughBidAdapter_spec.js +1 -1
- package/test/spec/modules/smartadserverBidAdapter_spec.js +42 -0
- package/test/spec/modules/stvBidAdapter_spec.js +13 -1
- package/test/spec/modules/undertoneBidAdapter_spec.js +57 -1
- package/test/spec/modules/userId_spec.js +80 -21
- package/test/spec/modules/yahoosspBidAdapter_spec.js +103 -51
- package/test/spec/modules/zeta_global_sspBidAdapter_spec.js +4 -0
- package/test/spec/unit/core/adapterManager_spec.js +181 -1
- package/test/spec/unit/core/storageManager_spec.js +76 -68
- package/test/spec/unit/pbjs_api_spec.js +15 -25
- package/test/spec/userSync_spec.js +45 -16
|
@@ -11,11 +11,16 @@ import {includes} from '../src/polyfill.js';
|
|
|
11
11
|
import {getRefererInfo} from '../src/refererDetection.js';
|
|
12
12
|
import {getStorageManager} from '../src/storageManager.js';
|
|
13
13
|
import {formatQS, isPlainObject, logError, parseUrl} from '../src/utils.js';
|
|
14
|
-
import {uspDataHandler} from '../src/adapterManager.js';
|
|
14
|
+
import {uspDataHandler, gppDataHandler} from '../src/adapterManager.js';
|
|
15
15
|
import {MODULE_TYPE_UID} from '../src/activities/modules.js';
|
|
16
16
|
|
|
17
17
|
const MODULE_NAME = 'connectId';
|
|
18
|
-
const STORAGE_EXPIRY_DAYS =
|
|
18
|
+
const STORAGE_EXPIRY_DAYS = 365;
|
|
19
|
+
const STORAGE_DURATION = 60 * 60 * 24 * 1000 * STORAGE_EXPIRY_DAYS;
|
|
20
|
+
const ID_EXPIRY_DAYS = 14;
|
|
21
|
+
const VALID_ID_DURATION = 60 * 60 * 24 * 1000 * ID_EXPIRY_DAYS;
|
|
22
|
+
const PUID_EXPIRY_DAYS = 30;
|
|
23
|
+
const PUID_EXPIRY = 60 * 60 * 24 * 1000 * PUID_EXPIRY_DAYS;
|
|
19
24
|
const VENDOR_ID = 25;
|
|
20
25
|
const PLACEHOLDER = '__PIXEL_ID__';
|
|
21
26
|
const UPS_ENDPOINT = `https://ups.analytics.yahoo.com/ups/${PLACEHOLDER}/fed`;
|
|
@@ -28,11 +33,11 @@ export const storage = getStorageManager({moduleType: MODULE_TYPE_UID, moduleNam
|
|
|
28
33
|
* @param {Object} obj
|
|
29
34
|
*/
|
|
30
35
|
function storeObject(obj) {
|
|
31
|
-
const expires = Date.now() +
|
|
36
|
+
const expires = Date.now() + STORAGE_DURATION;
|
|
32
37
|
if (storage.cookiesAreEnabled()) {
|
|
33
38
|
setEtldPlusOneCookie(MODULE_NAME, JSON.stringify(obj), new Date(expires), getSiteHostname());
|
|
34
|
-
}
|
|
35
|
-
|
|
39
|
+
}
|
|
40
|
+
if (storage.localStorageIsEnabled()) {
|
|
36
41
|
storage.setDataInLocalStorage(MODULE_NAME, JSON.stringify(obj));
|
|
37
42
|
}
|
|
38
43
|
}
|
|
@@ -75,7 +80,9 @@ function getIdFromLocalStorage() {
|
|
|
75
80
|
if (storedIdData) {
|
|
76
81
|
try {
|
|
77
82
|
storedIdData = JSON.parse(storedIdData);
|
|
78
|
-
} catch {
|
|
83
|
+
} catch (e) {
|
|
84
|
+
logError(`${MODULE_NAME} module: error while reading the local storage data.`);
|
|
85
|
+
}
|
|
79
86
|
if (isPlainObject(storedIdData) && storedIdData.__expires &&
|
|
80
87
|
storedIdData.__expires <= Date.now()) {
|
|
81
88
|
storage.removeDataFromLocalStorage(MODULE_NAME);
|
|
@@ -87,6 +94,34 @@ function getIdFromLocalStorage() {
|
|
|
87
94
|
return null;
|
|
88
95
|
}
|
|
89
96
|
|
|
97
|
+
function syncLocalStorageToCookie() {
|
|
98
|
+
if (!storage.cookiesAreEnabled()) {
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
const value = getIdFromLocalStorage();
|
|
102
|
+
const newCookieExpireTime = Date.now() + STORAGE_DURATION;
|
|
103
|
+
setEtldPlusOneCookie(MODULE_NAME, JSON.stringify(value), new Date(newCookieExpireTime), getSiteHostname());
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function isStale(storedIdData) {
|
|
107
|
+
if (isPlainObject(storedIdData) && storedIdData.lastSynced &&
|
|
108
|
+
(storedIdData.lastSynced + VALID_ID_DURATION) <= Date.now()) {
|
|
109
|
+
return true;
|
|
110
|
+
}
|
|
111
|
+
return false;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function getStoredId() {
|
|
115
|
+
let storedId = getIdFromCookie();
|
|
116
|
+
if (!storedId) {
|
|
117
|
+
storedId = getIdFromLocalStorage();
|
|
118
|
+
if (storedId && !isStale(storedId)) {
|
|
119
|
+
syncLocalStorageToCookie();
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
return storedId;
|
|
123
|
+
}
|
|
124
|
+
|
|
90
125
|
function getSiteHostname() {
|
|
91
126
|
const pageInfo = parseUrl(getRefererInfo().page);
|
|
92
127
|
return pageInfo.hostname;
|
|
@@ -127,16 +162,31 @@ export const connectIdSubmodule = {
|
|
|
127
162
|
return;
|
|
128
163
|
}
|
|
129
164
|
const params = config.params || {};
|
|
130
|
-
if (!params ||
|
|
165
|
+
if (!params ||
|
|
131
166
|
(typeof params.pixelId === 'undefined' && typeof params.endpoint === 'undefined')) {
|
|
132
|
-
logError(`${MODULE_NAME} module:
|
|
133
|
-
`least one of the 'he' or 'puid' parameters to be defined.`);
|
|
167
|
+
logError(`${MODULE_NAME} module: configuration requires the 'pixelId'.`);
|
|
134
168
|
return;
|
|
135
169
|
}
|
|
136
170
|
|
|
137
|
-
const storedId =
|
|
171
|
+
const storedId = getStoredId();
|
|
172
|
+
|
|
173
|
+
let shouldResync = isStale(storedId);
|
|
174
|
+
|
|
138
175
|
if (storedId) {
|
|
139
|
-
|
|
176
|
+
if (isPlainObject(storedId) && storedId.puid && storedId.lastUsed && !params.puid &&
|
|
177
|
+
(storedId.lastUsed + PUID_EXPIRY) <= Date.now()) {
|
|
178
|
+
delete storedId.puid;
|
|
179
|
+
shouldResync = true;
|
|
180
|
+
}
|
|
181
|
+
if ((params.he && params.he !== storedId.he) ||
|
|
182
|
+
(params.puid && params.puid !== storedId.puid)) {
|
|
183
|
+
shouldResync = true;
|
|
184
|
+
}
|
|
185
|
+
if (!shouldResync) {
|
|
186
|
+
storedId.lastUsed = Date.now();
|
|
187
|
+
storeObject(storedId);
|
|
188
|
+
return {id: storedId};
|
|
189
|
+
}
|
|
140
190
|
}
|
|
141
191
|
|
|
142
192
|
const uspString = uspDataHandler.getConsentData() || '';
|
|
@@ -148,6 +198,14 @@ export const connectIdSubmodule = {
|
|
|
148
198
|
us_privacy: uspString
|
|
149
199
|
};
|
|
150
200
|
|
|
201
|
+
const gppConsent = gppDataHandler.getConsentData();
|
|
202
|
+
if (gppConsent) {
|
|
203
|
+
data.gpp = `${gppConsent.gppString ? gppConsent.gppString : ''}`;
|
|
204
|
+
if (Array.isArray(gppConsent.applicableSections)) {
|
|
205
|
+
data.gpp_sid = gppConsent.applicableSections.join(',');
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
151
209
|
let topmostLocation = getRefererInfo().topmostLocation;
|
|
152
210
|
if (typeof topmostLocation === 'string') {
|
|
153
211
|
data.url = topmostLocation.split('?')[0];
|
|
@@ -159,6 +217,14 @@ export const connectIdSubmodule = {
|
|
|
159
217
|
}
|
|
160
218
|
});
|
|
161
219
|
|
|
220
|
+
const hashedEmail = params.he || storedId?.he;
|
|
221
|
+
if (hashedEmail) {
|
|
222
|
+
data.he = hashedEmail;
|
|
223
|
+
}
|
|
224
|
+
if (!data.puid && storedId?.puid) {
|
|
225
|
+
data.puid = storedId.puid;
|
|
226
|
+
}
|
|
227
|
+
|
|
162
228
|
const resp = function (callback) {
|
|
163
229
|
const callbacks = {
|
|
164
230
|
success: response => {
|
|
@@ -166,7 +232,12 @@ export const connectIdSubmodule = {
|
|
|
166
232
|
if (response) {
|
|
167
233
|
try {
|
|
168
234
|
responseObj = JSON.parse(response);
|
|
169
|
-
if (isPlainObject(responseObj) && Object.keys(responseObj).length > 0
|
|
235
|
+
if (isPlainObject(responseObj) && Object.keys(responseObj).length > 0 &&
|
|
236
|
+
(!!responseObj.connectId || !!responseObj.connectid)) {
|
|
237
|
+
responseObj.he = params.he;
|
|
238
|
+
responseObj.puid = params.puid || responseObj.puid;
|
|
239
|
+
responseObj.lastSynced = Date.now();
|
|
240
|
+
responseObj.lastUsed = Date.now();
|
|
170
241
|
storeObject(responseObj);
|
|
171
242
|
} else {
|
|
172
243
|
logError(`${MODULE_NAME} module: UPS response returned an invalid payload ${response}`);
|
|
@@ -186,7 +257,12 @@ export const connectIdSubmodule = {
|
|
|
186
257
|
let url = `${params.endpoint || endpoint}?${formatQS(data)}`;
|
|
187
258
|
connectIdSubmodule.getAjaxFn()(url, callbacks, null, {method: 'GET', withCredentials: true});
|
|
188
259
|
};
|
|
189
|
-
|
|
260
|
+
const result = {callback: resp};
|
|
261
|
+
if (shouldResync && storedId) {
|
|
262
|
+
result.id = storedId;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
return result;
|
|
190
266
|
},
|
|
191
267
|
|
|
192
268
|
/**
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
Yahoo ConnectID user ID Module.
|
|
4
4
|
|
|
5
|
+
*Note: The storage config should be ommited as the module handles the storage of the needed information.
|
|
6
|
+
|
|
5
7
|
### Prebid Params
|
|
6
8
|
|
|
7
9
|
```
|
|
@@ -9,11 +11,6 @@ pbjs.setConfig({
|
|
|
9
11
|
userSync: {
|
|
10
12
|
userIds: [{
|
|
11
13
|
name: 'connectId',
|
|
12
|
-
storage: {
|
|
13
|
-
name: 'connectId',
|
|
14
|
-
type: 'html5',
|
|
15
|
-
expires: 15
|
|
16
|
-
},
|
|
17
14
|
params: {
|
|
18
15
|
pixelId: 58776,
|
|
19
16
|
he: '0bef996248d63cea1529cb86de31e9547a712d9f380146e98bbd39beec70355a'
|
|
@@ -31,5 +28,5 @@ The below parameters apply only to the Yahoo ConnectID user ID Module.
|
|
|
31
28
|
| params | Required | Object | Container of all module params. ||
|
|
32
29
|
| params.pixelId | Required | Number |
|
|
33
30
|
The Yahoo-supplied publisher-specific pixel ID. | `"0000"` |
|
|
34
|
-
| params.he | Optional | String | The SHA-256 hashed user email address which has been lowercased prior to hashing.
|
|
35
|
-
| params.puid | Optional | String |
|
|
31
|
+
| params.he | Optional | String | The SHA-256 hashed user email address which has been lowercased prior to hashing. |`"ed8ddbf5a171981db8ef938596ca297d5e3f84bcc280041c5880dba3baf9c1d4"`|
|
|
32
|
+
| params.puid | Optional | String | A domain-specific user identifier such as a first-party cookie. If not passed, a puid value will be auto-generated and stored in local and / or cookie storage. | `"ab9iibf5a231ii1db8ef911596ca297d5e3f84biii00041c5880dba3baf9c1da"` |
|
|
@@ -541,6 +541,15 @@ function buildCdbRequest(context, bidRequests, bidderRequest) {
|
|
|
541
541
|
request.user.ext = request.user.ext || {};
|
|
542
542
|
request.user.ext.eids = [...userIdAsEids];
|
|
543
543
|
}
|
|
544
|
+
if (bidderRequest && bidderRequest.ortb2?.bcat) {
|
|
545
|
+
request.bcat = bidderRequest.ortb2.bcat;
|
|
546
|
+
}
|
|
547
|
+
if (bidderRequest && bidderRequest.ortb2?.badv) {
|
|
548
|
+
request.badv = bidderRequest.ortb2.badv;
|
|
549
|
+
}
|
|
550
|
+
if (bidderRequest && bidderRequest.ortb2?.bapp) {
|
|
551
|
+
request.bapp = bidderRequest.ortb2.bapp;
|
|
552
|
+
}
|
|
544
553
|
return request;
|
|
545
554
|
}
|
|
546
555
|
|
|
@@ -2,30 +2,42 @@
|
|
|
2
2
|
* This module gives publishers extra set of features to enforce individual purposes of TCF v2
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
import {deepAccess,
|
|
5
|
+
import {deepAccess, logError, logWarn} from '../src/utils.js';
|
|
6
6
|
import {config} from '../src/config.js';
|
|
7
7
|
import adapterManager, {gdprDataHandler} from '../src/adapterManager.js';
|
|
8
|
-
import {find
|
|
9
|
-
import {registerSyncInner} from '../src/adapters/bidderFactory.js';
|
|
8
|
+
import {find} from '../src/polyfill.js';
|
|
10
9
|
import {getHook} from '../src/hook.js';
|
|
11
|
-
import {validateStorageEnforcement} from '../src/storageManager.js';
|
|
12
10
|
import * as events from '../src/events.js';
|
|
13
11
|
import CONSTANTS from '../src/constants.json';
|
|
14
12
|
import {GDPR_GVLIDS, VENDORLESS_GVLID} from '../src/consentHandler.js';
|
|
15
13
|
import {
|
|
16
14
|
MODULE_TYPE_ANALYTICS,
|
|
17
15
|
MODULE_TYPE_BIDDER,
|
|
18
|
-
|
|
16
|
+
MODULE_TYPE_PREBID,
|
|
17
|
+
MODULE_TYPE_RTD,
|
|
19
18
|
MODULE_TYPE_UID
|
|
20
19
|
} from '../src/activities/modules.js';
|
|
20
|
+
import {
|
|
21
|
+
ACTIVITY_PARAM_ANL_CONFIG,
|
|
22
|
+
ACTIVITY_PARAM_COMPONENT_NAME,
|
|
23
|
+
ACTIVITY_PARAM_COMPONENT_TYPE
|
|
24
|
+
} from '../src/activities/params.js';
|
|
25
|
+
import {registerActivityControl} from '../src/activities/rules.js';
|
|
26
|
+
import {
|
|
27
|
+
ACTIVITY_ACCESS_DEVICE,
|
|
28
|
+
ACTIVITY_ENRICH_EIDS,
|
|
29
|
+
ACTIVITY_FETCH_BIDS,
|
|
30
|
+
ACTIVITY_REPORT_ANALYTICS,
|
|
31
|
+
ACTIVITY_SYNC_USER
|
|
32
|
+
} from '../src/activities/activities.js';
|
|
21
33
|
|
|
22
34
|
export const STRICT_STORAGE_ENFORCEMENT = 'strictStorageEnforcement';
|
|
23
35
|
|
|
24
36
|
const TCF2 = {
|
|
25
|
-
'purpose1': {
|
|
26
|
-
'purpose2': {
|
|
27
|
-
'purpose7': {
|
|
28
|
-
}
|
|
37
|
+
'purpose1': {id: 1, name: 'storage'},
|
|
38
|
+
'purpose2': {id: 2, name: 'basicAds'},
|
|
39
|
+
'purpose7': {id: 7, name: 'measurement'}
|
|
40
|
+
};
|
|
29
41
|
|
|
30
42
|
/*
|
|
31
43
|
These rules would be used if `consentManagement.gdpr.rules` is undefined by the publisher.
|
|
@@ -48,9 +60,9 @@ export let purpose7Rule;
|
|
|
48
60
|
|
|
49
61
|
export let enforcementRules;
|
|
50
62
|
|
|
51
|
-
const storageBlocked =
|
|
52
|
-
const biddersBlocked =
|
|
53
|
-
const analyticsBlocked =
|
|
63
|
+
const storageBlocked = new Set();
|
|
64
|
+
const biddersBlocked = new Set();
|
|
65
|
+
const analyticsBlocked = new Set();
|
|
54
66
|
|
|
55
67
|
let hooksAdded = false;
|
|
56
68
|
let strictStorageEnforcement = false;
|
|
@@ -62,6 +74,9 @@ const GVLID_LOOKUP_PRIORITY = [
|
|
|
62
74
|
MODULE_TYPE_RTD
|
|
63
75
|
];
|
|
64
76
|
|
|
77
|
+
const RULE_NAME = 'TCF2';
|
|
78
|
+
const RULE_HANDLES = [];
|
|
79
|
+
|
|
65
80
|
/**
|
|
66
81
|
* Retrieve a module's GVL ID.
|
|
67
82
|
*/
|
|
@@ -73,7 +88,7 @@ export function getGvlid(moduleType, moduleName, fallbackFn) {
|
|
|
73
88
|
// Return GVL ID from user defined gvlMapping
|
|
74
89
|
if (gvlMapping && gvlMapping[moduleName]) {
|
|
75
90
|
return gvlMapping[moduleName];
|
|
76
|
-
} else if (moduleType ===
|
|
91
|
+
} else if (moduleType === MODULE_TYPE_PREBID) {
|
|
77
92
|
return VENDORLESS_GVLID;
|
|
78
93
|
} else {
|
|
79
94
|
let {gvlid, modules} = GDPR_GVLIDS.get(moduleName);
|
|
@@ -83,8 +98,8 @@ export function getGvlid(moduleType, moduleName, fallbackFn) {
|
|
|
83
98
|
for (const type of GVLID_LOOKUP_PRIORITY) {
|
|
84
99
|
if (modules.hasOwnProperty(type)) {
|
|
85
100
|
gvlid = modules[type];
|
|
86
|
-
if (type !== moduleType
|
|
87
|
-
logWarn(`Multiple GVL IDs found for module '${moduleName}'; using the ${type} module's ID (${gvlid}) instead of the ${moduleType}'s ID (${modules[moduleType]})`)
|
|
101
|
+
if (type !== moduleType) {
|
|
102
|
+
logWarn(`Multiple GVL IDs found for module '${moduleName}'; using the ${type} module's ID (${gvlid}) instead of the ${moduleType}'s ID (${modules[moduleType]})`);
|
|
88
103
|
}
|
|
89
104
|
break;
|
|
90
105
|
}
|
|
@@ -109,9 +124,9 @@ export function getGvlidFromAnalyticsAdapter(code, config) {
|
|
|
109
124
|
try {
|
|
110
125
|
return gvlid.call(adapter.adapter, config);
|
|
111
126
|
} catch (e) {
|
|
112
|
-
logError(`Error invoking ${code} adapter.gvlid()`, e)
|
|
127
|
+
logError(`Error invoking ${code} adapter.gvlid()`, e);
|
|
113
128
|
}
|
|
114
|
-
})(adapter?.adapter?.gvlid)
|
|
129
|
+
})(adapter?.adapter?.gvlid);
|
|
115
130
|
}
|
|
116
131
|
|
|
117
132
|
export function shouldEnforce(consentData, purpose, name) {
|
|
@@ -120,7 +135,7 @@ export function shouldEnforce(consentData, purpose, name) {
|
|
|
120
135
|
// NOTE: this check is not foolproof, as when Prebid first loads, enforcement hooks have not been attached yet
|
|
121
136
|
// This piece of code would not run at all, and `gdprDataHandler.enabled` would be false, until the first
|
|
122
137
|
// `setConfig({consentManagement})`
|
|
123
|
-
logWarn(`Attempting operation that requires purpose ${purpose} consent while consent data is not available${name ? ` (module: ${name})` : ''}. Assuming no consent was given.`)
|
|
138
|
+
logWarn(`Attempting operation that requires purpose ${purpose} consent while consent data is not available${name ? ` (module: ${name})` : ''}. Assuming no consent was given.`);
|
|
124
139
|
return true;
|
|
125
140
|
}
|
|
126
141
|
return consentData && consentData.gdprApplies;
|
|
@@ -142,7 +157,7 @@ export function validateRules(rule, consentData, currentModule, gvlId) {
|
|
|
142
157
|
if ((rule.vendorExceptions || []).includes(currentModule)) {
|
|
143
158
|
return true;
|
|
144
159
|
}
|
|
145
|
-
const vendorConsentRequred = !((gvlId === VENDORLESS_GVLID || (rule.softVendorExceptions || []).includes(currentModule)))
|
|
160
|
+
const vendorConsentRequred = !((gvlId === VENDORLESS_GVLID || (rule.softVendorExceptions || []).includes(currentModule)));
|
|
146
161
|
|
|
147
162
|
// get data from the consent string
|
|
148
163
|
const purposeConsent = deepAccess(consentData, `vendorData.purpose.consents.${purposeId}`);
|
|
@@ -169,170 +184,80 @@ export function validateRules(rule, consentData, currentModule, gvlId) {
|
|
|
169
184
|
}
|
|
170
185
|
|
|
171
186
|
/**
|
|
172
|
-
*
|
|
187
|
+
* all activity rules follow the same structure:
|
|
188
|
+
* if GDPR is in scope, check configuration for a particular purpose, and if that enables enforcement,
|
|
189
|
+
* check against consent data for that purpose and vendor
|
|
173
190
|
*
|
|
174
|
-
* @param
|
|
175
|
-
* @param
|
|
176
|
-
* @param
|
|
177
|
-
* @param
|
|
178
|
-
* @param validate
|
|
191
|
+
* @param purposeNo TCF purpose number to check for this activity
|
|
192
|
+
* @param getEnforcementRule getter for gdprEnforcement rule definition to use
|
|
193
|
+
* @param blocked optional set to use for collecting denied vendors
|
|
194
|
+
* @param gvlidFallback optional factory function for a gvlid falllback function
|
|
179
195
|
*/
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
hasEnforcementHook: true
|
|
183
|
-
});
|
|
184
|
-
if (!hasDeviceAccess()) {
|
|
185
|
-
logWarn('Device access is disabled by Publisher');
|
|
186
|
-
result.valid = false;
|
|
187
|
-
} else if (moduleType === MODULE_TYPE_CORE && !strictStorageEnforcement) {
|
|
188
|
-
// for vendorless (core) storage, do not enforce rules unless strictStorageEnforcement is set
|
|
189
|
-
result.valid = true;
|
|
190
|
-
} else {
|
|
196
|
+
function gdprRule(purposeNo, getEnforcementRule, blocked = null, gvlidFallback = () => null) {
|
|
197
|
+
return function (params) {
|
|
191
198
|
const consentData = gdprDataHandler.getConsentData();
|
|
192
|
-
|
|
193
|
-
if (shouldEnforce(consentData,
|
|
194
|
-
const
|
|
195
|
-
|
|
196
|
-
if (
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
gvlid = getGvlid(moduleType, moduleName)
|
|
200
|
-
}
|
|
201
|
-
const curModule = moduleName || curBidder;
|
|
202
|
-
let isAllowed = validate(purpose1Rule, consentData, curModule, gvlid,);
|
|
203
|
-
if (isAllowed) {
|
|
204
|
-
result.valid = true;
|
|
205
|
-
} else {
|
|
206
|
-
curModule && logWarn(`TCF2 denied device access for ${curModule}`);
|
|
207
|
-
result.valid = false;
|
|
208
|
-
storageBlocked.push(curModule);
|
|
199
|
+
const modName = params[ACTIVITY_PARAM_COMPONENT_NAME];
|
|
200
|
+
if (shouldEnforce(consentData, purposeNo, modName)) {
|
|
201
|
+
const gvlid = getGvlid(params[ACTIVITY_PARAM_COMPONENT_TYPE], modName, gvlidFallback(params));
|
|
202
|
+
let allow = !!validateRules(getEnforcementRule(), consentData, modName, gvlid);
|
|
203
|
+
if (!allow) {
|
|
204
|
+
blocked && blocked.add(modName);
|
|
205
|
+
return {allow};
|
|
209
206
|
}
|
|
210
|
-
} else {
|
|
211
|
-
result.valid = true;
|
|
212
207
|
}
|
|
213
|
-
}
|
|
214
|
-
fn.call(this, moduleType, moduleName, result);
|
|
208
|
+
};
|
|
215
209
|
}
|
|
216
210
|
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
let isAllowed = validateRules(purpose1Rule, consentData, curBidder, gvlid);
|
|
228
|
-
if (isAllowed) {
|
|
229
|
-
fn.call(this, ...args);
|
|
230
|
-
} else {
|
|
231
|
-
logWarn(`User sync not allowed for ${curBidder}`);
|
|
232
|
-
storageBlocked.push(curBidder);
|
|
233
|
-
}
|
|
234
|
-
} else {
|
|
235
|
-
fn.call(this, ...args);
|
|
236
|
-
}
|
|
237
|
-
}
|
|
211
|
+
export const accessDeviceRule = ((rule) => {
|
|
212
|
+
return function (params) {
|
|
213
|
+
// for vendorless (core) storage, do not enforce rules unless strictStorageEnforcement is set
|
|
214
|
+
if (params[ACTIVITY_PARAM_COMPONENT_TYPE] === MODULE_TYPE_PREBID && !strictStorageEnforcement) return;
|
|
215
|
+
return rule(params);
|
|
216
|
+
};
|
|
217
|
+
})(gdprRule(1, () => purpose1Rule, storageBlocked));
|
|
218
|
+
|
|
219
|
+
export const syncUserRule = gdprRule(1, () => purpose1Rule, storageBlocked);
|
|
220
|
+
export const enrichEidsRule = gdprRule(1, () => purpose1Rule, storageBlocked);
|
|
238
221
|
|
|
239
|
-
/**
|
|
240
|
-
* This hook checks if user id module is given consent or not
|
|
241
|
-
* @param {Function} fn reference to original function (used by hook logic)
|
|
242
|
-
* @param {Submodule[]} submodules Array of user id submodules
|
|
243
|
-
* @param {Object} consentData GDPR consent data
|
|
244
|
-
*/
|
|
245
222
|
export function userIdHook(fn, submodules, consentData) {
|
|
223
|
+
// TODO: remove this in v8 (https://github.com/prebid/Prebid.js/issues/9766)
|
|
246
224
|
if (shouldEnforce(consentData, 1, 'User ID')) {
|
|
247
|
-
|
|
248
|
-
const moduleName = submodule.submodule.name;
|
|
249
|
-
const gvlid = getGvlid(MODULE_TYPE_UID, moduleName);
|
|
250
|
-
let isAllowed = validateRules(purpose1Rule, consentData, moduleName, gvlid);
|
|
251
|
-
if (isAllowed) {
|
|
252
|
-
return submodule;
|
|
253
|
-
} else {
|
|
254
|
-
logWarn(`User denied permission to fetch user id for ${moduleName} User id module`);
|
|
255
|
-
storageBlocked.push(moduleName);
|
|
256
|
-
}
|
|
257
|
-
return undefined;
|
|
258
|
-
}).filter(module => module)
|
|
259
|
-
fn.call(this, userIdModules, { ...consentData, hasValidated: true });
|
|
225
|
+
fn.call(this, submodules, {...consentData, hasValidated: true});
|
|
260
226
|
} else {
|
|
261
227
|
fn.call(this, submodules, consentData);
|
|
262
228
|
}
|
|
263
229
|
}
|
|
264
230
|
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
const consentData = gdprDataHandler.getConsentData();
|
|
273
|
-
if (shouldEnforce(consentData, 2)) {
|
|
274
|
-
adUnits.forEach(adUnit => {
|
|
275
|
-
adUnit.bids = adUnit.bids.filter(bid => {
|
|
276
|
-
const currBidder = bid.bidder;
|
|
277
|
-
const gvlId = getGvlid(MODULE_TYPE_BIDDER, currBidder);
|
|
278
|
-
if (includes(biddersBlocked, currBidder)) return false;
|
|
279
|
-
const isAllowed = !!validateRules(purpose2Rule, consentData, currBidder, gvlId);
|
|
280
|
-
if (!isAllowed) {
|
|
281
|
-
logWarn(`TCF2 blocked auction for ${currBidder}`);
|
|
282
|
-
biddersBlocked.push(currBidder);
|
|
283
|
-
}
|
|
284
|
-
return isAllowed;
|
|
285
|
-
});
|
|
286
|
-
});
|
|
287
|
-
fn.call(this, adUnits, ...args);
|
|
288
|
-
} else {
|
|
289
|
-
fn.call(this, adUnits, ...args);
|
|
290
|
-
}
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
/**
|
|
294
|
-
* Checks if Analytics adapters are allowed to send data to their servers for furhter processing.
|
|
295
|
-
* Enforces "purpose 7 (Measurement)" of TCF v2.0 spec
|
|
296
|
-
* @param {Function} fn - Function reference to the original function.
|
|
297
|
-
* @param {Array<AnalyticsAdapterConfig>} config - Configuration object passed to pbjs.enableAnalytics()
|
|
298
|
-
*/
|
|
299
|
-
export function enableAnalyticsHook(fn, config) {
|
|
300
|
-
const consentData = gdprDataHandler.getConsentData();
|
|
301
|
-
if (shouldEnforce(consentData, 7, 'Analytics')) {
|
|
302
|
-
if (!isArray(config)) {
|
|
303
|
-
config = [config]
|
|
231
|
+
export const fetchBidsRule = ((rule) => {
|
|
232
|
+
return function (params) {
|
|
233
|
+
if (params[ACTIVITY_PARAM_COMPONENT_TYPE] !== MODULE_TYPE_BIDDER) {
|
|
234
|
+
// TODO: this special case is for the PBS adapter (componentType is 'prebid')
|
|
235
|
+
// we should check for generic purpose 2 consent & vendor consent based on the PBS vendor's GVL ID;
|
|
236
|
+
// that is, however, a breaking change and skipped for now
|
|
237
|
+
return;
|
|
304
238
|
}
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
analyticsBlocked.push(analyticsAdapterCode);
|
|
311
|
-
logWarn(`TCF2 blocked analytics adapter ${conf.provider}`);
|
|
312
|
-
}
|
|
313
|
-
return isAllowed;
|
|
314
|
-
});
|
|
315
|
-
fn.call(this, config);
|
|
316
|
-
} else {
|
|
317
|
-
fn.call(this, config);
|
|
318
|
-
}
|
|
319
|
-
}
|
|
239
|
+
return rule(params);
|
|
240
|
+
};
|
|
241
|
+
})(gdprRule(2, () => purpose2Rule, biddersBlocked));
|
|
242
|
+
|
|
243
|
+
export const reportAnalyticsRule = gdprRule(7, () => purpose7Rule, analyticsBlocked, (params) => getGvlidFromAnalyticsAdapter(params[ACTIVITY_PARAM_COMPONENT_NAME], params[ACTIVITY_PARAM_ANL_CONFIG]));
|
|
320
244
|
|
|
321
245
|
/**
|
|
322
246
|
* Compiles the TCF2.0 enforcement results into an object, which is emitted as an event payload to "tcf2Enforcement" event.
|
|
323
247
|
*/
|
|
324
248
|
function emitTCF2FinalResults() {
|
|
325
249
|
// remove null and duplicate values
|
|
326
|
-
const
|
|
327
|
-
return
|
|
328
|
-
}
|
|
250
|
+
const formatSet = function (st) {
|
|
251
|
+
return Array.from(st.keys()).filter(el => el != null);
|
|
252
|
+
};
|
|
329
253
|
const tcf2FinalResults = {
|
|
330
|
-
storageBlocked:
|
|
331
|
-
biddersBlocked:
|
|
332
|
-
analyticsBlocked:
|
|
254
|
+
storageBlocked: formatSet(storageBlocked),
|
|
255
|
+
biddersBlocked: formatSet(biddersBlocked),
|
|
256
|
+
analyticsBlocked: formatSet(analyticsBlocked)
|
|
333
257
|
};
|
|
334
258
|
|
|
335
259
|
events.emit(CONSTANTS.EVENTS.TCF2_ENFORCEMENT, tcf2FinalResults);
|
|
260
|
+
[storageBlocked, biddersBlocked, analyticsBlocked].forEach(el => el.clear());
|
|
336
261
|
}
|
|
337
262
|
|
|
338
263
|
events.on(CONSTANTS.EVENTS.AUCTION_END, emitTCF2FinalResults);
|
|
@@ -340,9 +265,15 @@ events.on(CONSTANTS.EVENTS.AUCTION_END, emitTCF2FinalResults);
|
|
|
340
265
|
/*
|
|
341
266
|
Set of callback functions used to detect presence of a TCF rule, passed as the second argument to find().
|
|
342
267
|
*/
|
|
343
|
-
const hasPurpose1 = (rule) => {
|
|
344
|
-
|
|
345
|
-
|
|
268
|
+
const hasPurpose1 = (rule) => {
|
|
269
|
+
return rule.purpose === TCF2.purpose1.name;
|
|
270
|
+
};
|
|
271
|
+
const hasPurpose2 = (rule) => {
|
|
272
|
+
return rule.purpose === TCF2.purpose2.name;
|
|
273
|
+
};
|
|
274
|
+
const hasPurpose7 = (rule) => {
|
|
275
|
+
return rule.purpose === TCF2.purpose7.name;
|
|
276
|
+
};
|
|
346
277
|
|
|
347
278
|
/**
|
|
348
279
|
* A configuration function that initializes some module variables, as well as adds hooks
|
|
@@ -373,27 +304,25 @@ export function setEnforcementConfig(config) {
|
|
|
373
304
|
if (!hooksAdded) {
|
|
374
305
|
if (purpose1Rule) {
|
|
375
306
|
hooksAdded = true;
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
307
|
+
RULE_HANDLES.push(registerActivityControl(ACTIVITY_ACCESS_DEVICE, RULE_NAME, accessDeviceRule));
|
|
308
|
+
RULE_HANDLES.push(registerActivityControl(ACTIVITY_SYNC_USER, RULE_NAME, syncUserRule));
|
|
309
|
+
RULE_HANDLES.push(registerActivityControl(ACTIVITY_ENRICH_EIDS, RULE_NAME, enrichEidsRule));
|
|
310
|
+
// TODO: remove this hook in v8 (https://github.com/prebid/Prebid.js/issues/9766)
|
|
379
311
|
getHook('validateGdprEnforcement').before(userIdHook, 47);
|
|
380
312
|
}
|
|
381
313
|
if (purpose2Rule) {
|
|
382
|
-
|
|
314
|
+
RULE_HANDLES.push(registerActivityControl(ACTIVITY_FETCH_BIDS, RULE_NAME, fetchBidsRule));
|
|
383
315
|
}
|
|
384
316
|
if (purpose7Rule) {
|
|
385
|
-
|
|
317
|
+
RULE_HANDLES.push(registerActivityControl(ACTIVITY_REPORT_ANALYTICS, RULE_NAME, reportAnalyticsRule));
|
|
386
318
|
}
|
|
387
319
|
}
|
|
388
320
|
}
|
|
389
321
|
|
|
390
322
|
export function uninstall() {
|
|
323
|
+
while (RULE_HANDLES.length) RULE_HANDLES.pop()();
|
|
391
324
|
[
|
|
392
|
-
validateStorageEnforcement.getHooks({hook: deviceAccessHook}),
|
|
393
|
-
registerSyncInner.getHooks({hook: userSyncHook}),
|
|
394
325
|
getHook('validateGdprEnforcement').getHooks({hook: userIdHook}),
|
|
395
|
-
getHook('makeBidRequests').getHooks({hook: makeBidRequestsHook}),
|
|
396
|
-
getHook('enableAnalyticsCb').getHooks({hook: enableAnalyticsHook}),
|
|
397
326
|
].forEach(hook => hook.remove());
|
|
398
327
|
hooksAdded = false;
|
|
399
328
|
}
|