prebid.js 7.50.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.
Files changed (240) hide show
  1. package/dist/33acrossBidAdapter.js +1 -1
  2. package/dist/33acrossIdSystem.js +1 -1
  3. package/dist/adagioBidAdapter.js +1 -1
  4. package/dist/adbookpspBidAdapter.js +1 -1
  5. package/dist/adgenerationBidAdapter.js +1 -1
  6. package/dist/adhashBidAdapter.js +1 -1
  7. package/dist/adkernelBidAdapter.js +1 -1
  8. package/dist/admanBidAdapter.js +1 -1
  9. package/dist/adnuntiusBidAdapter.js +1 -1
  10. package/dist/adqueryBidAdapter.js +1 -1
  11. package/dist/adrelevantisBidAdapter.js +1 -1
  12. package/dist/adrinoBidAdapter.js +1 -1
  13. package/dist/adriverIdSystem.js +1 -1
  14. package/dist/adtelligentBidAdapter.js +1 -1
  15. package/dist/adtrgtmeBidAdapter.js +1 -1
  16. package/dist/adxcgBidAdapter.js +1 -1
  17. package/dist/adyoulikeBidAdapter.js +1 -1
  18. package/dist/ajaBidAdapter.js +1 -1
  19. package/dist/allowActivities.js +1 -0
  20. package/dist/amxBidAdapter.js +1 -1
  21. package/dist/amxIdSystem.js +1 -1
  22. package/dist/appierAnalyticsAdapter.js +1 -1
  23. package/dist/appnexusBidAdapter.js +1 -1
  24. package/dist/asoBidAdapter.js +1 -1
  25. package/dist/axonixBidAdapter.js +1 -1
  26. package/dist/bidglassBidAdapter.js +1 -1
  27. package/dist/big-richmediaBidAdapter.js +1 -1
  28. package/dist/bridgewellBidAdapter.js +1 -1
  29. package/dist/brightMountainMediaBidAdapter.js +1 -1
  30. package/dist/carodaBidAdapter.js +1 -1
  31. package/dist/chtnwBidAdapter.js +1 -1
  32. package/dist/cleanioRtdProvider.js +1 -1
  33. package/dist/concertBidAdapter.js +1 -1
  34. package/dist/connectIdSystem.js +1 -1
  35. package/dist/connectadBidAdapter.js +1 -1
  36. package/dist/consumableBidAdapter.js +1 -1
  37. package/dist/conversantAnalyticsAdapter.js +1 -1
  38. package/dist/conversantBidAdapter.js +1 -1
  39. package/dist/craftBidAdapter.js +1 -1
  40. package/dist/criteoBidAdapter.js +1 -1
  41. package/dist/cwireBidAdapter.js +1 -1
  42. package/dist/dependencies.json +6 -0
  43. package/dist/dspxBidAdapter.js +1 -1
  44. package/dist/eplanningBidAdapter.js +1 -1
  45. package/dist/feedadBidAdapter.js +1 -1
  46. package/dist/finativeBidAdapter.js +1 -1
  47. package/dist/freewheel-sspBidAdapter.js +1 -1
  48. package/dist/gdprEnforcement.js +1 -1
  49. package/dist/glimpseBidAdapter.js +1 -1
  50. package/dist/gmosspBidAdapter.js +1 -1
  51. package/dist/goldbachBidAdapter.js +1 -1
  52. package/dist/greenbidsAnalyticsAdapter.js +1 -1
  53. package/dist/greenbidsRtdProvider.js +1 -1
  54. package/dist/gridBidAdapter.js +1 -1
  55. package/dist/growthCodeRtdProvider.js +1 -0
  56. package/dist/gumgumBidAdapter.js +1 -1
  57. package/dist/h12mediaBidAdapter.js +1 -1
  58. package/dist/id5IdSystem.js +1 -1
  59. package/dist/improvedigitalBidAdapter.js +1 -1
  60. package/dist/inmarBidAdapter.js +1 -1
  61. package/dist/insticatorBidAdapter.js +1 -1
  62. package/dist/ixBidAdapter.js +1 -1
  63. package/dist/justpremiumBidAdapter.js +1 -1
  64. package/dist/kargoBidAdapter.js +1 -1
  65. package/dist/konduitAnalyticsAdapter.js +1 -1
  66. package/dist/kueezBidAdapter.js +1 -1
  67. package/dist/kueezRtbBidAdapter.js +1 -1
  68. package/dist/kulturemediaBidAdapter.js +1 -1
  69. package/dist/lassoBidAdapter.js +1 -1
  70. package/dist/lifestreetBidAdapter.js +1 -1
  71. package/dist/liveyieldAnalyticsAdapter.js +1 -1
  72. package/dist/logicadBidAdapter.js +1 -1
  73. package/dist/loglyliftBidAdapter.js +1 -1
  74. package/dist/magniteAnalyticsAdapter.js +1 -1
  75. package/dist/malltvAnalyticsAdapter.js +1 -1
  76. package/dist/marsmediaBidAdapter.js +1 -1
  77. package/dist/mediafuseBidAdapter.js +1 -1
  78. package/dist/mediasquareBidAdapter.js +1 -1
  79. package/dist/mgidBidAdapter.js +1 -1
  80. package/dist/minutemediaBidAdapter.js +1 -1
  81. package/dist/minutemediaplusBidAdapter.js +1 -1
  82. package/dist/nexx360BidAdapter.js +1 -1
  83. package/dist/not-for-prod/prebid.js +144 -140
  84. package/dist/objectGuard.js +1 -0
  85. package/dist/oguryBidAdapter.js +1 -1
  86. package/dist/onetagBidAdapter.js +1 -1
  87. package/dist/ooloAnalyticsAdapter.js +1 -1
  88. package/dist/optidigitalBidAdapter.js +1 -1
  89. package/dist/outbrainBidAdapter.js +1 -1
  90. package/dist/oxxionAnalyticsAdapter.js +1 -0
  91. package/dist/pairIdSystem.js +1 -1
  92. package/dist/parrableIdSystem.js +1 -1
  93. package/dist/pixfutureBidAdapter.js +1 -1
  94. package/dist/prebid-core.js +1 -1
  95. package/dist/prebidServerBidAdapter.js +1 -1
  96. package/dist/publinkIdSystem.js +1 -1
  97. package/dist/pubmaticBidAdapter.js +1 -1
  98. package/dist/pubwiseAnalyticsAdapter.js +1 -1
  99. package/dist/pxyzBidAdapter.js +1 -1
  100. package/dist/quantcastBidAdapter.js +1 -1
  101. package/dist/readpeakBidAdapter.js +1 -1
  102. package/dist/relaidoBidAdapter.js +1 -1
  103. package/dist/retailspotBidAdapter.js +1 -1
  104. package/dist/rhythmoneBidAdapter.js +1 -1
  105. package/dist/riseBidAdapter.js +1 -1
  106. package/dist/rtdModule.js +1 -1
  107. package/dist/rubiconAnalyticsAdapter.js +1 -1
  108. package/dist/rubiconBidAdapter.js +1 -1
  109. package/dist/seedingAllianceBidAdapter.js +1 -1
  110. package/dist/seedtagBidAdapter.js +1 -1
  111. package/dist/sharethroughAnalyticsAdapter.js +1 -1
  112. package/dist/sharethroughBidAdapter.js +1 -1
  113. package/dist/shinezBidAdapter.js +1 -1
  114. package/dist/smaatoBidAdapter.js +1 -1
  115. package/dist/smartadserverBidAdapter.js +1 -1
  116. package/dist/smartxBidAdapter.js +1 -1
  117. package/dist/smilewantedBidAdapter.js +1 -1
  118. package/dist/sonobiBidAdapter.js +1 -1
  119. package/dist/sovrnAnalyticsAdapter.js +1 -1
  120. package/dist/sovrnBidAdapter.js +1 -1
  121. package/dist/sspBCBidAdapter.js +1 -1
  122. package/dist/stvBidAdapter.js +1 -1
  123. package/dist/sublimeBidAdapter.js +1 -1
  124. package/dist/synacormediaBidAdapter.js +1 -1
  125. package/dist/targetVideoBidAdapter.js +1 -1
  126. package/dist/teadsBidAdapter.js +1 -1
  127. package/dist/trionBidAdapter.js +1 -1
  128. package/dist/tripleliftBidAdapter.js +1 -1
  129. package/dist/ttdBidAdapter.js +1 -1
  130. package/dist/ucfunnelAnalyticsAdapter.js +1 -1
  131. package/dist/uid2IdSystem.js +1 -1
  132. package/dist/underdogmediaBidAdapter.js +1 -1
  133. package/dist/undertoneBidAdapter.js +1 -1
  134. package/dist/userId.js +1 -1
  135. package/dist/vidazooBidAdapter.js +1 -1
  136. package/dist/videobyteBidAdapter.js +1 -1
  137. package/dist/visxBidAdapter.js +1 -1
  138. package/dist/vuukleBidAdapter.js +1 -1
  139. package/dist/widespaceBidAdapter.js +1 -1
  140. package/dist/winrBidAdapter.js +1 -1
  141. package/dist/yahoosspBidAdapter.js +1 -1
  142. package/dist/yieldmoBidAdapter.js +1 -1
  143. package/dist/yieldoneAnalyticsAdapter.js +1 -1
  144. package/dist/zeta_global_sspBidAdapter.js +1 -1
  145. package/integrationExamples/gpt/growthcode.html +20 -9
  146. package/libraries/objectGuard/objectGuard.js +108 -0
  147. package/libraries/objectGuard/ortbGuard.js +88 -0
  148. package/modules/adgenerationBidAdapter.js +14 -5
  149. package/modules/adhashBidAdapter.js +28 -17
  150. package/modules/adkernelBidAdapter.js +2 -1
  151. package/modules/admanBidAdapter.js +30 -22
  152. package/modules/adnuntiusBidAdapter.js +98 -79
  153. package/modules/adnuntiusBidAdapter.md +2 -1
  154. package/modules/adqueryBidAdapter.js +7 -1
  155. package/modules/adrinoBidAdapter.js +1 -0
  156. package/modules/adriverIdSystem.js +1 -1
  157. package/modules/adtelligentBidAdapter.js +4 -2
  158. package/modules/allowActivities.js +74 -0
  159. package/modules/appnexusBidAdapter.js +1 -0
  160. package/modules/cleanioRtdProvider.js +2 -4
  161. package/modules/connectIdSystem.js +89 -13
  162. package/modules/connectIdSystem.md +4 -7
  163. package/modules/criteoBidAdapter.js +50 -3
  164. package/modules/freewheel-sspBidAdapter.js +10 -2
  165. package/modules/gdprEnforcement.js +98 -169
  166. package/modules/growthCodeRtdProvider.js +131 -0
  167. package/modules/growthCodeRtdProvider.md +55 -0
  168. package/modules/gumgumBidAdapter.js +5 -0
  169. package/modules/ixBidAdapter.js +5 -2
  170. package/modules/minutemediaBidAdapter.js +13 -3
  171. package/modules/oxxionAnalyticsAdapter.js +212 -0
  172. package/modules/oxxionAnalyticsAdapter.md +33 -0
  173. package/modules/pairIdSystem.js +6 -6
  174. package/modules/prebidServerBidAdapter/index.js +7 -8
  175. package/modules/pubmaticBidAdapter.js +1 -0
  176. package/modules/riseBidAdapter.js +13 -3
  177. package/modules/rtbhouseBidAdapter.md +24 -0
  178. package/modules/rtdModule/index.js +12 -1
  179. package/modules/sharethroughBidAdapter.js +2 -2
  180. package/modules/smartadserverBidAdapter.js +5 -0
  181. package/modules/stvBidAdapter.js +34 -1
  182. package/modules/undertoneBidAdapter.js +9 -1
  183. package/modules/userId/index.js +69 -41
  184. package/modules/yahoosspBidAdapter.js +45 -3
  185. package/modules/yahoosspBidAdapter.md +1 -1
  186. package/modules/zeta_global_sspBidAdapter.js +1 -0
  187. package/package.json +1 -1
  188. package/src/activities/activities.js +47 -0
  189. package/src/activities/activityParams.js +8 -0
  190. package/src/activities/modules.js +1 -1
  191. package/src/activities/params.js +59 -0
  192. package/src/activities/redactor.js +157 -0
  193. package/src/activities/rules.js +95 -0
  194. package/src/adapterManager.js +45 -8
  195. package/src/adloader.js +2 -1
  196. package/src/fpd/rootDomain.js +1 -1
  197. package/src/native.js +20 -4
  198. package/src/prebid.js +1 -1
  199. package/src/storageManager.js +57 -44
  200. package/src/userSync.js +35 -18
  201. package/test/spec/activities/allowActivites_spec.js +138 -0
  202. package/test/spec/activities/objectGuard_spec.js +144 -0
  203. package/test/spec/activities/ortbGuard_spec.js +140 -0
  204. package/test/spec/activities/params_spec.js +25 -0
  205. package/test/spec/activities/redactor_spec.js +296 -0
  206. package/test/spec/activities/rules_spec.js +135 -0
  207. package/test/spec/modules/adgenerationBidAdapter_spec.js +52 -12
  208. package/test/spec/modules/adhashBidAdapter_spec.js +51 -1
  209. package/test/spec/modules/admanBidAdapter_spec.js +1 -2
  210. package/test/spec/modules/adnuntiusBidAdapter_spec.js +535 -264
  211. package/test/spec/modules/adqueryBidAdapter_spec.js +9 -0
  212. package/test/spec/modules/adrinoBidAdapter_spec.js +4 -0
  213. package/test/spec/modules/adtelligentBidAdapter_spec.js +1 -0
  214. package/test/spec/modules/cleanioRtdProvider_spec.js +7 -8
  215. package/test/spec/modules/connectIdSystem_spec.js +291 -23
  216. package/test/spec/modules/criteoBidAdapter_spec.js +134 -2
  217. package/test/spec/modules/freewheel-sspBidAdapter_spec.js +5 -2
  218. package/test/spec/modules/gdprEnforcement_spec.js +127 -414
  219. package/test/spec/modules/growthCodeRtdProvider_spec.js +127 -0
  220. package/test/spec/modules/gumgumBidAdapter_spec.js +14 -0
  221. package/test/spec/modules/ixBidAdapter_spec.js +2 -1
  222. package/test/spec/modules/minutemediaBidAdapter_spec.js +69 -1
  223. package/test/spec/modules/oxxionAnalyticsAdapter_spec.js +324 -0
  224. package/test/spec/modules/pairIdSystem_spec.js +16 -3
  225. package/test/spec/modules/prebidServerBidAdapter_spec.js +3 -1
  226. package/test/spec/modules/pubmaticBidAdapter_spec.js +7 -0
  227. package/test/spec/modules/realTimeDataModule_spec.js +1 -1
  228. package/test/spec/modules/riseBidAdapter_spec.js +69 -1
  229. package/test/spec/modules/sharethroughBidAdapter_spec.js +1 -1
  230. package/test/spec/modules/smartadserverBidAdapter_spec.js +42 -0
  231. package/test/spec/modules/stvBidAdapter_spec.js +13 -1
  232. package/test/spec/modules/undertoneBidAdapter_spec.js +57 -1
  233. package/test/spec/modules/userId_spec.js +80 -21
  234. package/test/spec/modules/yahoosspBidAdapter_spec.js +103 -51
  235. package/test/spec/modules/zeta_global_sspBidAdapter_spec.js +4 -0
  236. package/test/spec/native_spec.js +12 -12
  237. package/test/spec/unit/core/adapterManager_spec.js +181 -1
  238. package/test/spec/unit/core/storageManager_spec.js +76 -68
  239. package/test/spec/unit/pbjs_api_spec.js +15 -25
  240. package/test/spec/userSync_spec.js +45 -16
@@ -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, hasDeviceAccess, isArray, logError, logWarn} from '../src/utils.js';
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, includes} from '../src/polyfill.js';
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
- MODULE_TYPE_CORE, MODULE_TYPE_RTD,
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': { id: 1, name: 'storage' },
26
- 'purpose2': { id: 2, name: 'basicAds' },
27
- 'purpose7': { id: 7, name: 'measurement' }
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 === MODULE_TYPE_CORE) {
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 && !fallbackFn) {
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
- * This hook checks whether module has permission to access device or not. Device access include cookie and local storage
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 {Function} fn reference to original function (used by hook logic)
175
- * @param {string} moduleType type of the module
176
- * @param {string=} moduleName name of the module
177
- * @param result
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
- export function deviceAccessHook(fn, moduleType, moduleName, result, {validate = validateRules} = {}) {
181
- result = Object.assign({}, {
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
- let gvlid;
193
- if (shouldEnforce(consentData, 1, moduleName)) {
194
- const curBidder = config.getCurrentBidder();
195
- // Bidders have a copy of storage object with bidder code binded. Aliases will also pass the same bidder code when invoking storage functions and hence if alias tries to access device we will try to grab the gvl id for alias instead of original bidder
196
- if (curBidder && (curBidder !== moduleName) && adapterManager.aliasRegistry[curBidder] === moduleName) {
197
- gvlid = getGvlid(moduleType, curBidder);
198
- } else {
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
- * This hook checks if a bidder has consent for user sync or not
219
- * @param {Function} fn reference to original function (used by hook logic)
220
- * @param {...any} args args
221
- */
222
- export function userSyncHook(fn, ...args) {
223
- const consentData = gdprDataHandler.getConsentData();
224
- const curBidder = config.getCurrentBidder();
225
- if (shouldEnforce(consentData, 1, curBidder)) {
226
- const gvlid = getGvlid(MODULE_TYPE_BIDDER, curBidder);
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
- let userIdModules = submodules.map((submodule) => {
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
- * Checks if bidders are allowed in the auction.
267
- * Enforces "purpose 2 (Basic Ads)" of TCF v2.0 spec
268
- * @param {Function} fn - Function reference to the original function.
269
- * @param {Array<adUnits>} adUnits
270
- */
271
- export function makeBidRequestsHook(fn, adUnits, ...args) {
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
- config = config.filter(conf => {
306
- const analyticsAdapterCode = conf.provider;
307
- const gvlid = getGvlid(MODULE_TYPE_ANALYTICS, analyticsAdapterCode, () => getGvlidFromAnalyticsAdapter(analyticsAdapterCode, conf));
308
- const isAllowed = !!validateRules(purpose7Rule, consentData, analyticsAdapterCode, gvlid);
309
- if (!isAllowed) {
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 formatArray = function (arr) {
327
- return arr.filter((i, k) => i !== null && arr.indexOf(i) === k);
328
- }
250
+ const formatSet = function (st) {
251
+ return Array.from(st.keys()).filter(el => el != null);
252
+ };
329
253
  const tcf2FinalResults = {
330
- storageBlocked: formatArray(storageBlocked),
331
- biddersBlocked: formatArray(biddersBlocked),
332
- analyticsBlocked: formatArray(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) => { return rule.purpose === TCF2.purpose1.name }
344
- const hasPurpose2 = (rule) => { return rule.purpose === TCF2.purpose2.name }
345
- const hasPurpose7 = (rule) => { return rule.purpose === TCF2.purpose7.name }
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
- validateStorageEnforcement.before(deviceAccessHook, 49);
377
- registerSyncInner.before(userSyncHook, 48);
378
- // Using getHook as user id and gdprEnforcement are both optional modules. Using import will auto include the file in build
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
- getHook('makeBidRequests').before(makeBidRequestsHook);
314
+ RULE_HANDLES.push(registerActivityControl(ACTIVITY_FETCH_BIDS, RULE_NAME, fetchBidsRule));
383
315
  }
384
316
  if (purpose7Rule) {
385
- getHook('enableAnalyticsCb').before(enableAnalyticsHook);
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
  }
@@ -0,0 +1,131 @@
1
+ /**
2
+ * This module adds GrowthCode HEM and other Data to Bid Requests
3
+ * @module modules/growthCodeRtdProvider
4
+ */
5
+ import { submodule } from '../src/hook.js'
6
+ import { getStorageManager } from '../src/storageManager.js';
7
+ import {
8
+ logMessage, logError, tryAppendQueryString, mergeDeep
9
+ } from '../src/utils.js';
10
+ import * as ajax from '../src/ajax.js';
11
+ import { MODULE_TYPE_RTD } from '../src/activities/modules.js';
12
+
13
+ const MODULE_NAME = 'growthCodeRtd';
14
+ const LOG_PREFIX = 'GrowthCodeRtd: ';
15
+ const ENDPOINT_URL = 'https://p2.gcprivacy.com/v2/rtd?'
16
+ const RTD_EXPIRE_KEY = 'gc_rtd_expires_at'
17
+ const RTD_CACHE_KEY = 'gc_rtd_items'
18
+
19
+ export const storage = getStorageManager({ moduleType: MODULE_TYPE_RTD, moduleName: MODULE_NAME });
20
+ let items
21
+
22
+ export const growthCodeRtdProvider = {
23
+ name: MODULE_NAME,
24
+ init: init,
25
+ getBidRequestData: alterBidRequests,
26
+ addData: addData,
27
+ callServer: callServer
28
+ };
29
+
30
+ /**
31
+ * Parse json if possible, else return null
32
+ * @param data
33
+ * @returns {any|null}
34
+ */
35
+ function tryParse(data) {
36
+ try {
37
+ return JSON.parse(data);
38
+ } catch (err) {
39
+ logError(err);
40
+ return null;
41
+ }
42
+ }
43
+
44
+ /**
45
+ * Init The RTD Module
46
+ * @param config
47
+ * @param userConsent
48
+ * @returns {boolean}
49
+ */
50
+ function init(config, userConsent) {
51
+ logMessage(LOG_PREFIX + 'Init RTB');
52
+
53
+ if (config == null) {
54
+ return false
55
+ }
56
+
57
+ const configParams = (config && config.params) || {};
58
+ let expiresAt = parseInt(storage.getDataFromLocalStorage(RTD_EXPIRE_KEY, null));
59
+
60
+ items = tryParse(storage.getDataFromLocalStorage(RTD_CACHE_KEY, null));
61
+
62
+ return callServer(configParams, items, expiresAt, userConsent);
63
+ }
64
+ function callServer(configParams, items, expiresAt, userConsent) {
65
+ // Expire Cache
66
+ let now = Math.trunc(Date.now() / 1000);
67
+ if ((!isNaN(expiresAt)) && (now > expiresAt)) {
68
+ expiresAt = NaN;
69
+ storage.removeDataFromLocalStorage(RTD_CACHE_KEY, null)
70
+ storage.removeDataFromLocalStorage(RTD_EXPIRE_KEY, null)
71
+ }
72
+ if ((items === null) && (isNaN(expiresAt))) {
73
+ let gcid = localStorage.getItem('gcid')
74
+
75
+ let url = configParams.url ? configParams.url : ENDPOINT_URL;
76
+ url = tryAppendQueryString(url, 'pid', configParams.pid);
77
+ url = tryAppendQueryString(url, 'u', window.location.href);
78
+ url = tryAppendQueryString(url, 'gcid', gcid);
79
+ if ((userConsent !== null) && (userConsent.gdpr !== null) && (userConsent.gdpr.consentData.getTCData.tcString)) {
80
+ url = tryAppendQueryString(url, 'tcf', userConsent.gdpr.consentData.getTCData.tcString)
81
+ }
82
+
83
+ ajax.ajaxBuilder()(url, {
84
+ success: response => {
85
+ let respJson = tryParse(response);
86
+ // If response is a valid json and should save is true
87
+ if (respJson && respJson.results >= 1) {
88
+ storage.setDataInLocalStorage(RTD_CACHE_KEY, JSON.stringify(respJson.items), null);
89
+ storage.setDataInLocalStorage(RTD_EXPIRE_KEY, respJson.expires_at, null)
90
+ } else {
91
+ storage.setDataInLocalStorage(RTD_EXPIRE_KEY, respJson.expires_at, null)
92
+ }
93
+ },
94
+ error: error => {
95
+ logError(LOG_PREFIX + 'ID fetch encountered an error', error);
96
+ }
97
+ }, undefined, {method: 'GET', withCredentials: true})
98
+ }
99
+
100
+ return true;
101
+ }
102
+
103
+ function addData(reqBidsConfigObj, items) {
104
+ let merge = false
105
+
106
+ for (let j = 0; j < items.length; j++) {
107
+ let item = items[j]
108
+ let data = JSON.parse(item.parameters);
109
+ if (item['attachment_point'] === 'data') {
110
+ mergeDeep(reqBidsConfigObj.ortb2Fragments.bidder, data)
111
+ merge = true
112
+ }
113
+ }
114
+ return merge
115
+ }
116
+
117
+ /**
118
+ * Alter the Bid Request for additional information such as HEM or 3rd Party Ids
119
+ * @param reqBidsConfigObj
120
+ * @param callback
121
+ * @param config
122
+ * @param userConsent
123
+ */
124
+ function alterBidRequests(reqBidsConfigObj, callback, config, userConsent) {
125
+ if (items != null) {
126
+ addData(reqBidsConfigObj, items)
127
+ }
128
+ callback();
129
+ }
130
+
131
+ submodule('realTimeData', growthCodeRtdProvider);
@@ -0,0 +1,55 @@
1
+ ## GrowthCode Real-time Data Submodule
2
+
3
+ The [GrowthCode](https://growthcode.io) real-time data module in Prebid enables publishers to fully
4
+ leverage the potential of their first-party audiences and contextual data.
5
+ With an integrated cookieless GrowthCode identity, this module offers real-time
6
+ contextual and audience segmentation (IAB Taxonomy 2.2, cattax: 6) capabilities, and HEMs that can seamlessly
7
+ integrate into your existing Prebid deployment, making it easy to maximize
8
+ your advertising strategies.
9
+
10
+ ## Building Prebid with GrowthCode Support
11
+
12
+ Compile the GrowthCode RTD module into your Prebid build:
13
+
14
+ `gulp serve --modules=userId,rtdModule,appnexusBidAdapter,growthCodeRtdProvider,sharedIdSystem,criteoBidAdapter`
15
+
16
+ Please visit https://growthcode.io/ for more information.
17
+
18
+ ```
19
+ pbjs.setConfig(
20
+ ...
21
+ realTimeData: {
22
+ auctionDelay: 1000,
23
+ dataProviders: [
24
+ {
25
+ name: 'growthCodeRtd',
26
+ waitForIt: true,
27
+ params: {
28
+ pid: 'TEST01',
29
+ }
30
+ }
31
+ ]
32
+ }
33
+ ...
34
+ }
35
+ ```
36
+
37
+ ### Parameter Descriptions for the GrowthCode Configuration Section
38
+
39
+ | Name | Type | Description | Notes |
40
+ |:---------------------------------|:--------|:--------------------------------------------------------------------------|:----------------------------|
41
+ | name | String | Real time data module name | Always 'growthCodeRtd' |
42
+ | waitForIt | Boolean | Required to ensure that the auction is delayed until prefetch is complete | Optional. Defaults to false |
43
+ | params | Object | | |
44
+ | params.pid | String | This is the Parter ID value obtained from GrowthCode | `TEST01` |
45
+ | params.url | String | Custom URL for server | Optional |
46
+
47
+ ## Testing
48
+
49
+ To view an example of GrowthCode backends:
50
+
51
+ `gulp serve --modules=userId,rtdModule,appnexusBidAdapter,growthCodeRtdProvider,sharedIdSystem,criteoBidAdapter`
52
+
53
+ and then point your browser at:
54
+
55
+ `http://localhost:9999/integrationExamples/gpt/growthcode.html`
@@ -312,6 +312,11 @@ function buildRequests(validBidRequests, bidderRequest) {
312
312
  data.lt = lt;
313
313
  data.to = to;
314
314
 
315
+ // ADJS-1286 Read id5 id linktype field
316
+ if (userId && userId.id5id && userId.id5id.uid && userId.id5id.ext) {
317
+ data.id5Id = userId.id5id.uid || null
318
+ data.id5IdLinkType = userId.id5id.ext.linkType || null
319
+ }
315
320
  // ADTS-169 add adUnitCode to requests
316
321
  if (adUnitCode) data.aun = adUnitCode;
317
322
 
@@ -145,6 +145,7 @@ export const FEATURE_TOGGLES = {
145
145
  let siteID = 0;
146
146
  let gdprConsent = '';
147
147
  let usPrivacy = '';
148
+ let defaultVideoPlacement = false;
148
149
 
149
150
  // Possible values for bidResponse.seatBid[].bid[].mtype which indicates the type of the creative markup so that it can properly be associated with the right sub-object of the BidRequest.Imp.
150
151
  const MEDIA_TYPES = {
@@ -223,7 +224,8 @@ function bidToVideoImp(bid) {
223
224
  if (deepAccess(videoParamRef, 'playerConfig.floatOnScroll')) {
224
225
  imp.video.placement = 5;
225
226
  } else {
226
- imp.video.placement = 4;
227
+ imp.video.placement = 3;
228
+ defaultVideoPlacement = true;
227
229
  }
228
230
  } else {
229
231
  logWarn(`IX Bid Adapter: Video context '${context}' is not supported`);
@@ -1111,7 +1113,8 @@ function buildIXDiag(validBidRequests) {
1111
1113
  ren: false,
1112
1114
  version: '$prebid.version$',
1113
1115
  userIds: _getUserIds(validBidRequests[0]),
1114
- url: window.location.href.split('?')[0]
1116
+ url: window.location.href.split('?')[0],
1117
+ vpd: defaultVideoPlacement
1115
1118
  };
1116
1119
 
1117
1120
  // create ad unit map and collect the required diag properties
@@ -307,6 +307,16 @@ function generateBidParameters(bid, bidderRequest) {
307
307
  bidObject.placementId = placementId;
308
308
  }
309
309
 
310
+ const sua = deepAccess(bid, `ortb2.device.sua`);
311
+ if (sua) {
312
+ bidObject.sua = sua;
313
+ }
314
+
315
+ const coppa = deepAccess(bid, `ortb2.regs.coppa`)
316
+ if (coppa) {
317
+ bidObject.coppa = 1;
318
+ }
319
+
310
320
  if (mediaType === VIDEO) {
311
321
  const playbackMethod = deepAccess(bid, `mediaTypes.video.playbackmethod`);
312
322
  let playbackMethodValue;
@@ -348,9 +358,9 @@ function generateBidParameters(bid, bidderRequest) {
348
358
  bidObject.linearity = linearity;
349
359
  }
350
360
 
351
- const coppa = deepAccess(bid, `ortb2.regs.coppa`);
352
- if (coppa) {
353
- bidObject.coppa = 1;
361
+ const plcmt = deepAccess(bid, `mediaTypes.video.plcmt`);
362
+ if (plcmt) {
363
+ bidObject.plcmt = plcmt;
354
364
  }
355
365
  }
356
366