forked from prebid/Prebid.js
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathhumansecurityRtdProvider.js
180 lines (157 loc) · 5.39 KB
/
humansecurityRtdProvider.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
/**
* This module adds humansecurity provider to the real time data module
*
* The {@link module:modules/realTimeData} module is required
* The module will inject the HUMAN Security script into the context where Prebid.js is initialized, enriching bid requests with specific data to provide advanced protection against ad fraud and spoofing.
* @module modules/humansecurityRtdProvider
* @requires module:modules/realTimeData
*/
import { submodule } from '../src/hook.js';
import {
prefixLog,
mergeDeep,
generateUUID,
getWindowSelf,
} from '../src/utils.js';
import { getRefererInfo } from '../src/refererDetection.js';
import { getGlobal } from '../src/prebidGlobal.js';
import { loadExternalScript } from '../src/adloader.js';
import { MODULE_TYPE_RTD } from '../src/activities/modules.js';
/**
* @typedef {import('../modules/rtdModule/index.js').RtdSubmodule} RtdSubmodule
* @typedef {import('../modules/rtdModule/index.js').SubmoduleConfig} SubmoduleConfig
* @typedef {import('../modules/rtdModule/index.js').UserConsentData} UserConsentData
*/
const SUBMODULE_NAME = 'humansecurity';
const SCRIPT_URL = 'https://sonar.script.ac/prebid/rtd.js';
const { logInfo, logWarn, logError } = prefixLog(`[${SUBMODULE_NAME}]:`);
/** @type {string} */
let clientId = '';
/** @type {boolean} */
let verbose = false;
/** @type {string} */
let sessionId = '';
/** @type {Object} */
let hmnsData = { };
/**
* Submodule registration
*/
function main() {
submodule('realTimeData', /** @type {RtdSubmodule} */ ({
name: SUBMODULE_NAME,
//
init: (config, userConsent) => {
try {
load(config);
return true;
} catch (err) {
logError('init', err.message);
return false;
}
},
getBidRequestData: onGetBidRequestData
}));
}
/**
* Injects HUMAN Security script on the page to facilitate pre-bid signal collection.
* @param {SubmoduleConfig} config
*/
function load(config) {
// By default, this submodule loads the generic implementation script
// only identified by the referrer information. In the future, if publishers
// want to have analytics where their websites are grouped, they can request
// Client ID from HUMAN, pass it here, and it will enable advanced reporting
clientId = config?.params?.clientId || '';
if (clientId && (typeof clientId !== 'string' || !/^\w{3,16}$/.test(clientId))) {
throw new Error(`The 'clientId' parameter must be a short alphanumeric string`);
}
// Load/reset the state
verbose = !!config?.params?.verbose;
sessionId = generateUUID();
hmnsData = {};
// We rely on prebid implementation to get the best domain possible here
// In some cases, it still might be null, though
const refDomain = getRefererInfo().domain || '';
// Once loaded, the implementation script will publish an API using
// the session ID value it was given in data attributes
const scriptAttrs = { 'data-sid': sessionId };
const scriptUrl = `${SCRIPT_URL}?r=${refDomain}${clientId ? `&c=${clientId}` : ''}`;
loadExternalScript(scriptUrl, MODULE_TYPE_RTD, SUBMODULE_NAME, onImplLoaded, null, scriptAttrs);
}
/**
* The callback to loadExternalScript
* Establishes the bridge between this RTD submodule and the loaded implementation
*/
function onImplLoaded() {
// We then get a hold on this script using the knowledge of this session ID
const wnd = getWindowSelf();
const impl = wnd[`sonar_${sessionId}`];
if (typeof impl !== 'object' || typeof impl.connect !== 'function') {
verbose && logWarn('onload', 'Unable to access the implementation script');
return;
}
// And set up a bridge between the RTD submodule and the implementation.
// The first argument is used to identify the caller.
// The callback might be called multiple times to update the signals
// once more precise information is available.
impl.connect(getGlobal(), onImplMessage);
}
/**
* The bridge function will be called by the implementation script
* to update the token information or report errors
* @param {Object} msg
*/
function onImplMessage(msg) {
if (typeof msg !== 'object') {
return;
}
switch (msg.type) {
case 'hmns': {
hmnsData = mergeDeep({}, msg.data || {});
break;
}
case 'error': {
logError('impl', msg.data || '');
break;
}
case 'warn': {
verbose && logWarn('impl', msg.data || '');
break;
}
case 'info': {
verbose && logInfo('impl', msg.data || '');
break;
}
}
}
/**
* onGetBidRequestData is called once per auction.
* Update the `ortb2Fragments` object with the data from the injected script.
*
* @param {Object} reqBidsConfigObj
* @param {function} callback
* @param {SubmoduleConfig} config
* @param {UserConsentData} userConsent
*/
function onGetBidRequestData(reqBidsConfigObj, callback, config, userConsent) {
// Add device.ext.hmns to the global ORTB data for all vendors to use
// At the time of writing this submodule, "hmns" is an object defined
// internally by humansecurity, and it currently contains "v1" field
// with a token that contains collected signals about this session.
mergeDeep(reqBidsConfigObj.ortb2Fragments.global, { device: { ext: { hmns: hmnsData } } });
callback();
}
/**
* Exporting local (and otherwise encapsulated to this module) functions
* for testing purposes
*/
export const __TEST__ = {
SUBMODULE_NAME,
SCRIPT_URL,
main,
load,
onImplLoaded,
onImplMessage,
onGetBidRequestData
};
main();