• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

prebid / Prebid.js / 19437775255

17 Nov 2025 05:00PM UTC coverage: 96.213% (-0.02%) from 96.231%
19437775255

push

github

web-flow
sevioBidAdapter_bugfix: Send all sizes instead of just maxSize (#14133)

* Send all sizes instead of just maxSize

* Added tests to cover modifs in the sizes that we are sending

53222 of 65234 branches covered (81.59%)

10 of 10 new or added lines in 2 files covered. (100.0%)

304 existing lines in 58 files now uncovered.

202715 of 210693 relevant lines covered (96.21%)

71.77 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

92.81
/modules/connectIdSystem.js
1
/**
1✔
2
 * This module adds support for Yahoo ConnectID to the user ID module system.
3
 * The {@link module:modules/userId} module is required
4
 * @module modules/connectIdSystem
5
 * @requires module:modules/userId
6
 */
7

8
import {ajax} from '../src/ajax.js';
9
import {submodule} from '../src/hook.js';
10

11
import {getRefererInfo} from '../src/refererDetection.js';
12
import {getStorageManager, STORAGE_TYPE_COOKIES, STORAGE_TYPE_LOCALSTORAGE} from '../src/storageManager.js';
13
import {formatQS, isNumber, isPlainObject, logError, parseUrl} from '../src/utils.js';
14
import {MODULE_TYPE_UID} from '../src/activities/modules.js';
15

16
/**
17
 * @typedef {import('../modules/userId/index.js').Submodule} Submodule
18
 * @typedef {import('../modules/userId/index.js').SubmoduleConfig} SubmoduleConfig
19
 * @typedef {import('../modules/userId/index.js').ConsentData} ConsentData
20
 * @typedef {import('../modules/userId/index.js').IdResponse} IdResponse
21
 */
22

23
const MODULE_NAME = 'connectId';
1✔
24
const STORAGE_EXPIRY_DAYS = 365;
1✔
25
const STORAGE_DURATION = 60 * 60 * 24 * 1000 * STORAGE_EXPIRY_DAYS;
1✔
26
const ID_EXPIRY_DAYS = 14;
1✔
27
const VALID_ID_DURATION = 60 * 60 * 24 * 1000 * ID_EXPIRY_DAYS;
1✔
28
const PUID_EXPIRY_DAYS = 30;
1✔
29
const PUID_EXPIRY = 60 * 60 * 24 * 1000 * PUID_EXPIRY_DAYS;
1✔
30
const VENDOR_ID = 25;
1✔
31
const PLACEHOLDER = '__PIXEL_ID__';
1✔
32
const UPS_ENDPOINT = `https://ups.analytics.yahoo.com/ups/${PLACEHOLDER}/fed`;
1✔
33
const OVERRIDE_OPT_OUT_KEY = 'connectIdOptOut';
1✔
34
const INPUT_PARAM_KEYS = ['pixelId', 'he', 'puid'];
1✔
35
const O_AND_O_DOMAINS = [
1✔
36
  'yahoo.com',
37
  'aol.com',
38
  'aol.ca',
39
  'aol.de',
40
  'aol.co.uk',
41
  'engadget.com',
42
  'techcrunch.com',
43
  'autoblog.com',
44
];
45
export const storage = getStorageManager({moduleType: MODULE_TYPE_UID, moduleName: MODULE_NAME});
1✔
46

47
/**
48
 * Stores the ConnectID object in browser storage according to storage configuration
49
 * @function
50
 * @param {Object} obj - The ID object to store
51
 * @param {Object} [storageConfig={}] - Storage configuration
52
 * @param {string} [storageConfig.type] - Storage type: 'cookie', 'html5', or 'cookie&html5'
53
 */
54
function storeObject(obj, storageConfig = {}) {
12!
55
  const expires = Date.now() + STORAGE_DURATION;
12✔
56
  const storageType = storageConfig.type || '';
12✔
57

58
  const useCookie = !storageType || storageType.includes(STORAGE_TYPE_COOKIES);
12✔
59
  const useLocalStorage = !storageType || storageType.includes(STORAGE_TYPE_LOCALSTORAGE);
12✔
60

61
  if (useCookie && storage.cookiesAreEnabled()) {
12✔
62
    setEtldPlusOneCookie(MODULE_NAME, JSON.stringify(obj), new Date(expires), getSiteHostname());
9✔
63
  }
64
  if (useLocalStorage && storage.localStorageIsEnabled()) {
12✔
65
    storage.setDataInLocalStorage(MODULE_NAME, JSON.stringify(obj));
11✔
66
  }
67
}
68

69
/**
70
 * Attempts to store a cookie on eTLD + 1
71
 *
72
 * @function
73
 * @param {String} key
74
 * @param {String} value
75
 * @param {Date} expirationDate
76
 * @param {String} hostname
77
 */
78
function setEtldPlusOneCookie(key, value, expirationDate, hostname) {
79
  const subDomains = hostname.split('.');
13✔
80
  for (let i = 0; i < subDomains.length; ++i) {
13✔
81
    const domain = subDomains.slice(subDomains.length - i - 1, subDomains.length).join('.');
13✔
82
    try {
13✔
83
      storage.setCookie(key, value, expirationDate.toUTCString(), null, '.' + domain);
13✔
84
      const storedCookie = storage.getCookie(key);
13✔
85
      if (storedCookie && storedCookie === value) {
13!
UNCOV
86
        break;
×
87
      }
88
    } catch (error) {}
89
  }
90
}
91

92
function getIdFromCookie() {
93
  if (storage.cookiesAreEnabled()) {
46✔
94
    try {
45✔
95
      return JSON.parse(storage.getCookie(MODULE_NAME));
45✔
96
    } catch {}
97
  }
98
  return null;
30✔
99
}
100

101
function getIdFromLocalStorage() {
102
  if (storage.localStorageIsEnabled()) {
34✔
103
    let storedIdData = storage.getDataFromLocalStorage(MODULE_NAME);
34✔
104
    if (storedIdData) {
34✔
105
      try {
10✔
106
        storedIdData = JSON.parse(storedIdData);
10✔
107
      } catch (e) {
108
        logError(`${MODULE_NAME} module: error while reading the local storage data.`);
8✔
109
      }
110
      if (isPlainObject(storedIdData) && storedIdData.__expires &&
10✔
111
          storedIdData.__expires <= Date.now()) {
112
        storage.removeDataFromLocalStorage(MODULE_NAME);
1✔
113
        return null;
1✔
114
      }
115
      return storedIdData;
9✔
116
    }
117
  }
118
  return null;
24✔
119
}
120

121
/**
122
 * Syncs ID from localStorage to cookie if storage configuration allows
123
 * @function
124
 * @param {Object} [storageConfig={}] - Storage configuration
125
 * @param {string} [storageConfig.type] - Storage type: 'cookie', 'html5', or 'cookie&html5'
126
 */
127
function syncLocalStorageToCookie(storageConfig = {}) {
5!
128
  const storageType = storageConfig.type || '';
5✔
129
  const useCookie = !storageType || storageType.includes(STORAGE_TYPE_COOKIES);
5✔
130

131
  if (!useCookie || !storage.cookiesAreEnabled()) {
5✔
132
    return;
1✔
133
  }
134
  const value = getIdFromLocalStorage();
4✔
135
  const newCookieExpireTime = Date.now() + STORAGE_DURATION;
4✔
136
  setEtldPlusOneCookie(MODULE_NAME, JSON.stringify(value), new Date(newCookieExpireTime), getSiteHostname());
4✔
137
}
138

139
function isStale(storedIdData) {
140
  if (isOAndOTraffic()) {
51✔
141
    return true;
1✔
142
  } else if (isPlainObject(storedIdData) && storedIdData.lastSynced) {
50✔
143
    const validTTL = storedIdData.ttl || VALID_ID_DURATION;
12✔
144
    return storedIdData.lastSynced + validTTL <= Date.now();
12✔
145
  }
146
  return false;
38✔
147
}
148

149
/**
150
 * Retrieves stored ConnectID from cookie or localStorage
151
 * @function
152
 * @param {Object} [storageConfig={}] - Storage configuration
153
 * @param {string} [storageConfig.type] - Storage type: 'cookie', 'html5', or 'cookie&html5'
154
 * @returns {Object|null} The stored ID object or null if not found
155
 */
156
function getStoredId(storageConfig = {}) {
46!
157
  let storedId = getIdFromCookie();
46✔
158
  if (!storedId) {
46✔
159
    storedId = getIdFromLocalStorage();
30✔
160
    if (storedId && !isStale(storedId)) {
30✔
161
      syncLocalStorageToCookie(storageConfig);
5✔
162
    }
163
  }
164
  return storedId;
46✔
165
}
166

167
function getSiteHostname() {
168
  const pageInfo = parseUrl(getRefererInfo().page);
13✔
169
  return pageInfo.hostname;
13✔
170
}
171

172
function isOAndOTraffic() {
173
  let referer = getRefererInfo().ref;
51✔
174

175
  if (referer) {
51✔
176
    referer = parseUrl(referer).hostname;
1✔
177
    const subDomains = referer.split('.');
1✔
178
    referer = subDomains.slice(subDomains.length - 2, subDomains.length).join('.');
1✔
179
  }
180
  return O_AND_O_DOMAINS.indexOf(referer) >= 0;
51✔
181
}
182

183
/** @type {Submodule} */
184
export const connectIdSubmodule = {
1✔
185
  /**
186
   * used to link submodule with config
187
   * @type {string}
188
   */
189
  name: MODULE_NAME,
190
  /**
191
   * @type {Number}
192
   */
193
  gvlid: VENDOR_ID,
194
  /**
195
   * decode the stored id value for passing to bid requests
196
   * @function
197
   * @returns {{connectId: string} | undefined}
198
   */
199
  decode(value) {
200
    if (connectIdSubmodule.userHasOptedOut()) {
6✔
201
      return undefined;
1✔
202
    }
203
    return (isPlainObject(value) && (value.connectId || value.connectid))
5✔
204
      ? {connectId: value.connectId || value.connectid} : undefined;
3✔
205
  },
206
  /**
207
   * Gets the Yahoo ConnectID
208
   * @function
209
   * @param {SubmoduleConfig} [config]
210
   * @param {ConsentData} [consentData]
211
   * @returns {IdResponse|undefined}
212
   */
213
  getId(config, consentData) {
214
    if (connectIdSubmodule.userHasOptedOut()) {
49✔
215
      return;
1✔
216
    }
217
    const params = config.params || {};
48!
218
    const storageConfig = config.storage || {};
48✔
219
    if (!params ||
48✔
220
        (typeof params.pixelId === 'undefined' && typeof params.endpoint === 'undefined')) {
221
      logError(`${MODULE_NAME} module: configuration requires the 'pixelId'.`);
2✔
222
      return;
2✔
223
    }
224

225
    const storedId = getStoredId(storageConfig);
46✔
226

227
    let shouldResync = isStale(storedId);
46✔
228

229
    if (storedId) {
46✔
230
      if (isPlainObject(storedId) && storedId.puid && storedId.lastUsed && !params.puid &&
21✔
231
        (storedId.lastUsed + PUID_EXPIRY) <= Date.now()) {
232
        delete storedId.puid;
2✔
233
        shouldResync = true;
2✔
234
      }
235
      if ((params.he && params.he !== storedId.he) ||
21✔
236
        (params.puid && params.puid !== storedId.puid)) {
237
        shouldResync = true;
11✔
238
      }
239
      if (!shouldResync) {
21✔
240
        storedId.lastUsed = Date.now();
7✔
241
        storeObject(storedId, storageConfig);
7✔
242
        return {id: storedId};
7✔
243
      }
244
    }
245

246
    const uspString = consentData.usp || '';
39!
247
    const data = {
39✔
248
      v: '1',
249
      '1p': [1, '1', true].includes(params['1p']) ? '1' : '0',
39✔
250
      gdpr: connectIdSubmodule.isEUConsentRequired(consentData?.gdpr) ? '1' : '0',
156!
251
      gdpr_consent: connectIdSubmodule.isEUConsentRequired(consentData?.gdpr) ? consentData.gdpr.consentString : '',
156!
252
      us_privacy: uspString
253
    };
254

255
    const gppConsent = consentData.gpp;
39✔
256
    if (gppConsent) {
39✔
257
      data.gpp = `${gppConsent.gppString ? gppConsent.gppString : ''}`;
38!
258
      if (Array.isArray(gppConsent.applicableSections)) {
38✔
259
        data.gpp_sid = gppConsent.applicableSections.join(',');
38✔
260
      }
261
    }
262

263
    const topmostLocation = getRefererInfo().topmostLocation;
39✔
264
    if (typeof topmostLocation === 'string') {
39✔
265
      data.url = topmostLocation.split('?')[0];
38✔
266
    }
267

268
    INPUT_PARAM_KEYS.forEach(key => {
39✔
269
      if (typeof params[key] !== 'undefined') {
117✔
270
        data[key] = params[key];
72✔
271
      }
272
    });
273

274
    const hashedEmail = params.he || storedId?.he;
39✔
275
    if (hashedEmail) {
39✔
276
      data.he = hashedEmail;
31✔
277
    }
278
    if (!data.puid && storedId?.puid) {
39✔
279
      data.puid = storedId.puid;
3✔
280
    }
281

282
    const resp = function (callback) {
39✔
283
      const callbacks = {
39✔
284
        success: response => {
285
          let responseObj;
286
          if (response) {
5✔
287
            try {
5✔
288
              responseObj = JSON.parse(response);
5✔
289
              if (isPlainObject(responseObj) && Object.keys(responseObj).length > 0 &&
5!
290
                 (!!responseObj.connectId || !!responseObj.connectid)) {
291
                responseObj.he = params.he;
5✔
292
                responseObj.puid = params.puid || responseObj.puid;
5!
293
                responseObj.lastSynced = Date.now();
5✔
294
                responseObj.lastUsed = Date.now();
5✔
295
                if (isNumber(responseObj.ttl)) {
5!
UNCOV
296
                  let validTTLMiliseconds = responseObj.ttl * 60 * 60 * 1000;
×
UNCOV
297
                  if (validTTLMiliseconds > VALID_ID_DURATION) {
×
UNCOV
298
                    validTTLMiliseconds = VALID_ID_DURATION;
×
299
                  }
UNCOV
300
                  responseObj.ttl = validTTLMiliseconds;
×
301
                }
302
                storeObject(responseObj, storageConfig);
5✔
303
              } else {
UNCOV
304
                logError(`${MODULE_NAME} module: UPS response returned an invalid payload ${response}`);
×
305
              }
306
            } catch (error) {
UNCOV
307
              logError(error);
×
308
            }
309
          }
310
          callback(responseObj);
5✔
311
        },
312
        error: error => {
UNCOV
313
          logError(`${MODULE_NAME} module: ID fetch encountered an error`, error);
×
UNCOV
314
          callback();
×
315
        }
316
      };
317
      const endpoint = UPS_ENDPOINT.replace(PLACEHOLDER, params.pixelId);
39✔
318
      const url = `${params.endpoint || endpoint}?${formatQS(data)}`;
39✔
319
      connectIdSubmodule.getAjaxFn()(url, callbacks, null, {method: 'GET', withCredentials: true});
39✔
320
    };
321
    const result = {callback: resp};
39✔
322
    if (shouldResync && storedId) {
39✔
323
      result.id = storedId;
14✔
324
    }
325

326
    return result;
39✔
327
  },
328

329
  /**
330
   * Utility function that returns a boolean flag indicating if the opportunity
331
   * is subject to GDPR
332
   * @returns {Boolean}
333
   */
334
  isEUConsentRequired(consentData) {
335
    return !!(consentData?.gdprApplies);
82✔
336
  },
337

338
  /**
339
   * Utility function that returns a boolean flag indicating if the user
340
   * has opted out via the Yahoo easy-opt-out mechanism.
341
   * @returns {Boolean}
342
   */
343
  userHasOptedOut() {
344
    try {
52✔
345
      if (storage.localStorageIsEnabled()) {
52!
346
        return storage.getDataFromLocalStorage(OVERRIDE_OPT_OUT_KEY) === '1';
52✔
347
      } else {
UNCOV
348
        return true;
×
349
      }
350
    } catch {
UNCOV
351
      return false;
×
352
    }
353
  },
354

355
  /**
356
   * Return the function used to perform XHR calls.
357
   * Utilised for each of testing.
358
   * @returns {Function}
359
   */
360
  getAjaxFn() {
361
    return ajax;
6✔
362
  },
363
  eids: {
364
    'connectId': {
365
      source: 'yahoo.com',
366
      atype: 3
367
    },
368
  }
369
};
370

371
submodule('userId', connectIdSubmodule);
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc