@@ -8,7 +8,13 @@ import {
8
8
import { gppDataHandler } from '../../src/adapterManager.js' ;
9
9
10
10
// default interpretation for MSPA consent(s):
11
- // https://docs.google.com/document/d/1yPEVx3bBdSkIw-QNkQR5qi1ztmn9zoXk-LaGQB6iw7Q
11
+ // https://docs.prebid.org/features/mspa-usnat.html
12
+
13
+ const SENSITIVE_DATA_GEO = 7 ;
14
+
15
+ function isApplicable ( val ) {
16
+ return val != null && val !== 0
17
+ }
12
18
13
19
export function isBasicConsentDenied ( cd ) {
14
20
// service provider mode is always consent denied
@@ -18,47 +24,60 @@ export function isBasicConsentDenied(cd) {
18
24
// minors 13+ who have not given consent
19
25
cd . KnownChildSensitiveDataConsents [ 0 ] === 1 ||
20
26
// minors under 13 cannot consent
21
- cd . KnownChildSensitiveDataConsents [ 1 ] !== 0 ||
22
- // sensitive category consent impossible without notice
23
- ( cd . SensitiveDataProcessing . some ( element => element === 2 ) && ( cd . SensitiveDataLimitUseNotice !== 1 || cd . SensitiveDataProcessingOptOutNotice !== 1 ) ) ;
27
+ isApplicable ( cd . KnownChildSensitiveDataConsents [ 1 ] ) ;
24
28
}
25
29
26
- export function isSensitiveNoticeMissing ( cd ) {
27
- return [ 'SensitiveDataProcessingOptOutNotice' , 'SensitiveDataLimitUseNotice' ] . some ( prop => cd [ prop ] === 2 )
30
+ export function sensitiveNoticeIs ( cd , value ) {
31
+ return [ 'SensitiveDataProcessingOptOutNotice' , 'SensitiveDataLimitUseNotice' ] . some ( prop => cd [ prop ] === value )
28
32
}
29
33
30
34
export function isConsentDenied ( cd ) {
31
35
return isBasicConsentDenied ( cd ) ||
32
- // user opts out
33
- ( [ 'SaleOptOut' , 'SharingOptOut' , 'TargetedAdvertisingOptOut' ] . some ( prop => cd [ prop ] === 1 ) ) ||
34
- // notice not given
35
- ( [ 'SaleOptOutNotice' , 'SharingNotice' , 'SharingOptOutNotice' , 'TargetedAdvertisingOptOutNotice' ] . some ( prop => cd [ prop ] === 2 ) ) ||
36
- // sale opted in but notice does not apply
37
- ( ( cd . SaleOptOut === 2 && cd . SaleOptOutNotice === 0 ) ) ||
38
- // targeted opted in but notice does not apply
39
- ( ( cd . TargetedAdvertisingOptOut === 2 && cd . TargetedAdvertisingOptOutNotice === 0 ) ) ||
40
- // sharing opted in but notices do not apply
41
- ( ( cd . SharingOptOut === 2 && ( cd . SharingNotice === 0 || cd . SharingOptOutNotice === 0 ) ) ) ;
36
+ [ 'Sale' , 'Sharing' , 'TargetedAdvertising' ] . some ( scope => {
37
+ const oo = cd [ `${ scope } OptOut` ] ;
38
+ const notice = cd [ `${ scope } OptOutNotice` ] ;
39
+ // user opted out
40
+ return oo === 1 ||
41
+ // opt-out notice was not given
42
+ notice === 2 ||
43
+ // do not trust CMP if it signals opt-in but no opt-out notice was given
44
+ ( oo === 2 && notice === 0 ) ;
45
+ } ) ||
46
+ // no sharing notice was given ...
47
+ cd . SharingNotice === 2 ||
48
+ // ... or the CMP says it was not applicable, while also claiming it got consent
49
+ ( cd . SharingOptOut === 2 && cd . SharingNotice === 0 ) ;
42
50
}
43
51
44
- export function isTransmitUfpdConsentDenied ( cd ) {
45
- // SensitiveDataProcessing[1-5,11]=1 OR SensitiveDataProcessing[6,7,9,10,12]<>0 OR
46
- const mustBeZero = [ 6 , 7 , 9 , 10 , 12 ] ;
47
- const mustBeZeroSubtractedVector = mustBeZero . map ( ( number ) => number - 1 ) ;
48
- const SensitiveDataProcessingMustBeZero = cd . SensitiveDataProcessing . filter ( index => mustBeZeroSubtractedVector . includes ( index ) ) ;
49
- const cannotBeOne = [ 1 , 2 , 3 , 4 , 5 , 11 ] ;
50
- const cannotBeOneSubtractedVector = cannotBeOne . map ( ( number ) => number - 1 ) ;
51
- const SensitiveDataProcessingCannotBeOne = cd . SensitiveDataProcessing . filter ( index => cannotBeOneSubtractedVector . includes ( index ) ) ;
52
- return isConsentDenied ( cd ) ||
53
- isSensitiveNoticeMissing ( cd ) ||
54
- SensitiveDataProcessingCannotBeOne . some ( val => val === 1 ) ||
55
- SensitiveDataProcessingMustBeZero . some ( val => val !== 0 ) ;
56
- }
52
+ export const isTransmitUfpdConsentDenied = ( ( ) => {
53
+ // deny anything that smells like: genetic, biometric, state/national ID, financial, union membership,
54
+ // or personal communication data
55
+ const cannotBeInScope = [ 6 , 7 , 9 , 10 , 12 ] . map ( el => -- el ) ;
56
+ // require consent for everything else (except geo, which is treated separately)
57
+ const allExceptGeo = Array . from ( Array ( 12 ) . keys ( ) ) . filter ( ( el ) => el !== SENSITIVE_DATA_GEO )
58
+ const mustHaveConsent = allExceptGeo . filter ( el => ! cannotBeInScope . includes ( el ) ) ;
59
+
60
+ return function ( cd ) {
61
+ return isConsentDenied ( cd ) ||
62
+ // no notice about sensitive data was given
63
+ sensitiveNoticeIs ( cd , 2 ) ||
64
+ // extra-sensitive data is applicable
65
+ cannotBeInScope . some ( i => isApplicable ( cd . SensitiveDataProcessing [ i ] ) ) ||
66
+ // user opted out for not-as-sensitive data
67
+ mustHaveConsent . some ( i => cd . SensitiveDataProcessing [ i ] === 1 ) ||
68
+ // CMP says it has consent, but did not give notice about it
69
+ ( sensitiveNoticeIs ( cd , 0 ) && allExceptGeo . some ( i => cd . SensitiveDataProcessing [ i ] === 2 ) )
70
+ }
71
+ } ) ( ) ;
57
72
58
73
export function isTransmitGeoConsentDenied ( cd ) {
59
- return isBasicConsentDenied ( cd ) ||
60
- isSensitiveNoticeMissing ( cd ) ||
61
- cd . SensitiveDataProcessing [ 7 ] === 1
74
+ const geoConsent = cd . SensitiveDataProcessing [ SENSITIVE_DATA_GEO ] ;
75
+ return geoConsent === 1 ||
76
+ isBasicConsentDenied ( cd ) ||
77
+ // no sensitive data notice was given
78
+ sensitiveNoticeIs ( cd , 2 ) ||
79
+ // do not trust CMP if it says it has consent for geo but didn't show a sensitive data notice
80
+ ( sensitiveNoticeIs ( cd , 0 ) && geoConsent === 2 )
62
81
}
63
82
64
83
const CONSENT_RULES = {
0 commit comments