-
Notifications
You must be signed in to change notification settings - Fork 97
/
Copy pathSemanticPreEncoder.ts
201 lines (135 loc) · 6.18 KB
/
SemanticPreEncoder.ts
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
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
import {EncodingError} from '../errors/index.js';
import {TCModel} from '../TCModel.js';
import {EncodingOptions} from './EncodingOptions.js';
import {Vector, RestrictionType} from '../model/index.js';
import {GVL} from '../GVL.js';
type ProcessorFunction = (tcModel: TCModel, gvl: GVL) => TCModel;
export class SemanticPreEncoder {
private static processor: ProcessorFunction[] = [
(tcModel: TCModel): TCModel => tcModel,
(tcModel: TCModel, gvl: GVL): TCModel => {
/**
* in case this wasn't set previously. This should filter out invalid
* purpose restrictions.
*/
tcModel.publisherRestrictions.gvl = gvl;
/**
* Purpose 1 is never allowed to be true for legitimate interest
* As of TCF v2.2 purposes 3,4,5 & 6 are not allowed to be true for LI
*/
tcModel.purposeLegitimateInterests.unset([1, 3, 4, 5, 6]);
/**
* If a Vendor does not declare a purpose for consent or legitimate
* interest they should not have a positive signal for it. This code
* removes positive signals created mistakingly.
*/
const vectorToIntMap = new Map<string, Vector>();
vectorToIntMap.set('legIntPurposes', tcModel.vendorLegitimateInterests);
vectorToIntMap.set('purposes', tcModel.vendorConsents);
vectorToIntMap.forEach((vector, gvlVendorKey) => {
vector.forEach((value: boolean, vendorId: number): void => {
if (value) {
const vendor = gvl.vendors[vendorId];
if (!vendor || vendor.deletedDate) {
/**
* If the vendor doesn't exist, then they should not receive a
* positive signal
*/
vector.unset(vendorId);
} else if (vendor[gvlVendorKey].length === 0) {
if (
gvlVendorKey === 'legIntPurposes' && vendor['purposes'].length === 0 && vendor['legIntPurposes'].length === 0 && vendor['specialPurposes'].length > 0
) {
/**
* Per June 2021 Policy change, Vendors declaring only Special Purposes must
* have their legitimate interest Vendor bit set if they have been disclosed.
* This empty block ensures their LI bit remains set
*/
} else {
/**
* If the vendor does exist, but they haven't declared any
* purposes for this legal basis, then we need to see if they can
* possibly have the legal basis from their flexible purposes.
*/
if (tcModel.isServiceSpecific) {
if (vendor.flexiblePurposes.length === 0) {
/**
* No flexible basis for any purposes, so we can safely remove
* this vendor from the legal basis.
*/
vector.unset(vendorId);
} else {
/**
* They have some flexible purposes, we should check for a
* publisher restriction value that would enable this vendor to
* have the override-preferred basis.
*/
const restrictions = tcModel.publisherRestrictions.getRestrictions(vendorId);
let isValid = false;
for (let i = 0, len = restrictions.length; i < len && !isValid; i ++) {
/**
* If this condition is true the loop will break. If we are
* dealing with the consent purposes ('purposes') and the
* publisher restriction overrides to consent then it is
* valid for the vendor to have a positive signal for
* consent. Likewise for legitimate interest purposes
* ('legIntPurposes') and requiring legitimate interest.
*/
isValid = (
(restrictions[i].restrictionType === RestrictionType.REQUIRE_CONSENT &&
gvlVendorKey === 'purposes') ||
(restrictions[i].restrictionType === RestrictionType.REQUIRE_LI &&
gvlVendorKey === 'legIntPurposes'));
}
if (!isValid) {
/**
* if we came through the previous loop without finding a
* valid reasing: no overriding restrictions (changes in
* legal basis) then it's not valid for this vendor to have
* this legal basis.
*/
vector.unset(vendorId);
}
}
} else {
/**
* This is a globally-scoped string so flexible purposes will not
* be able to change this value because purposeRestrictions only
* apply to service-specific strings.
*/
vector.unset(vendorId);
}
}
}
}
});
});
tcModel.vendorsDisclosed.set(gvl.vendors);
return tcModel;
},
];
public static process(tcModel: TCModel, options?: EncodingOptions): TCModel {
const gvl = tcModel.gvl;
if (!gvl) {
throw new EncodingError('Unable to encode TCModel without a GVL');
}
if (!gvl.isReady) {
throw new EncodingError('Unable to encode TCModel tcModel.gvl.readyPromise is not resolved');
}
tcModel = tcModel.clone();
tcModel.consentLanguage = gvl.language.slice(0, 2).toUpperCase();
if (options?.version > 0 && options?.version <= this.processor.length) {
tcModel.version = options.version;
} else {
/**
* this is equal to the latest or most current version
*/
tcModel.version = this.processor.length;
}
const processorFunctionIndex = tcModel.version - 1;
if (!this.processor[processorFunctionIndex]) {
throw new EncodingError(`Invalid version: ${tcModel.version}`);
}
return this.processor[processorFunctionIndex](tcModel, gvl);
}
}