github.com/prebid/prebid-server/v2@v2.18.0/privacy/scrubber_test.go (about) 1 package privacy 2 3 import ( 4 "encoding/json" 5 "testing" 6 7 "github.com/prebid/openrtb/v20/openrtb2" 8 "github.com/prebid/prebid-server/v2/openrtb_ext" 9 "github.com/prebid/prebid-server/v2/util/ptrutil" 10 "github.com/stretchr/testify/assert" 11 ) 12 13 func TestScrubDeviceIDs(t *testing.T) { 14 testCases := []struct { 15 name string 16 deviceIn *openrtb2.Device 17 expectedDevice *openrtb2.Device 18 }{ 19 { 20 name: "all", 21 deviceIn: &openrtb2.Device{DIDMD5: "MD5", DIDSHA1: "SHA1", DPIDMD5: "MD5", DPIDSHA1: "SHA1", IFA: "IFA", MACMD5: "MD5", MACSHA1: "SHA1"}, 22 expectedDevice: &openrtb2.Device{DIDMD5: "", DIDSHA1: "", DPIDMD5: "", DPIDSHA1: "", IFA: "", MACMD5: "", MACSHA1: ""}, 23 }, 24 { 25 name: "nil", 26 deviceIn: nil, 27 expectedDevice: nil, 28 }, 29 } 30 for _, test := range testCases { 31 t.Run(test.name, func(t *testing.T) { 32 brw := &openrtb_ext.RequestWrapper{BidRequest: &openrtb2.BidRequest{Device: test.deviceIn}} 33 scrubDeviceIDs(brw) 34 brw.RebuildRequest() 35 assert.Equal(t, test.expectedDevice, brw.Device) 36 }) 37 } 38 } 39 40 func TestScrubUserIDs(t *testing.T) { 41 testCases := []struct { 42 name string 43 userIn *openrtb2.User 44 expectedUser *openrtb2.User 45 }{ 46 { 47 name: "all", 48 userIn: &openrtb2.User{Data: []openrtb2.Data{}, ID: "ID", BuyerUID: "bID", Yob: 2000, Gender: "M", Keywords: "keywords", KwArray: nil}, 49 expectedUser: &openrtb2.User{Data: nil, ID: "", BuyerUID: "", Yob: 0, Gender: "", Keywords: "", KwArray: nil}, 50 }, 51 { 52 name: "nil", 53 userIn: nil, 54 expectedUser: nil, 55 }, 56 } 57 for _, test := range testCases { 58 t.Run(test.name, func(t *testing.T) { 59 brw := &openrtb_ext.RequestWrapper{BidRequest: &openrtb2.BidRequest{User: test.userIn}} 60 scrubUserIDs(brw) 61 brw.RebuildRequest() 62 assert.Equal(t, test.expectedUser, brw.User) 63 }) 64 } 65 } 66 67 func TestScrubUserDemographics(t *testing.T) { 68 testCases := []struct { 69 name string 70 userIn *openrtb2.User 71 expectedUser *openrtb2.User 72 }{ 73 { 74 name: "all", 75 userIn: &openrtb2.User{ID: "ID", BuyerUID: "bID", Yob: 2000, Gender: "M"}, 76 expectedUser: &openrtb2.User{ID: "", BuyerUID: "", Yob: 0, Gender: ""}, 77 }, 78 { 79 name: "nil", 80 userIn: nil, 81 expectedUser: nil, 82 }, 83 } 84 for _, test := range testCases { 85 t.Run(test.name, func(t *testing.T) { 86 brw := &openrtb_ext.RequestWrapper{BidRequest: &openrtb2.BidRequest{User: test.userIn}} 87 scrubUserDemographics(brw) 88 brw.RebuildRequest() 89 assert.Equal(t, test.expectedUser, brw.User) 90 }) 91 } 92 } 93 94 func TestScrubUserExt(t *testing.T) { 95 testCases := []struct { 96 name string 97 userIn *openrtb2.User 98 fieldName string 99 expectedUser *openrtb2.User 100 }{ 101 { 102 name: "nil_user", 103 userIn: nil, 104 expectedUser: nil, 105 }, 106 { 107 name: "nil_ext", 108 userIn: &openrtb2.User{ID: "ID", Ext: nil}, 109 expectedUser: &openrtb2.User{ID: "ID", Ext: nil}, 110 }, 111 { 112 name: "empty_ext", 113 userIn: &openrtb2.User{ID: "ID", Ext: json.RawMessage(`{}`)}, 114 expectedUser: &openrtb2.User{ID: "ID", Ext: json.RawMessage(`{}`)}, 115 }, 116 { 117 name: "ext_with_field", 118 userIn: &openrtb2.User{ID: "ID", Ext: json.RawMessage(`{"data":"123","test":1}`)}, 119 fieldName: "data", 120 expectedUser: &openrtb2.User{ID: "ID", Ext: json.RawMessage(`{"test":1}`)}, 121 }, 122 { 123 name: "ext_without_field", 124 userIn: &openrtb2.User{ID: "ID", Ext: json.RawMessage(`{"data":"123","test":1}`)}, 125 fieldName: "noData", 126 expectedUser: &openrtb2.User{ID: "ID", Ext: json.RawMessage(`{"data":"123","test":1}`)}, 127 }, 128 { 129 name: "nil", 130 userIn: nil, 131 expectedUser: nil, 132 }, 133 } 134 for _, test := range testCases { 135 t.Run(test.name, func(t *testing.T) { 136 brw := &openrtb_ext.RequestWrapper{BidRequest: &openrtb2.BidRequest{User: test.userIn}} 137 scrubUserExt(brw, test.fieldName) 138 brw.RebuildRequest() 139 assert.Equal(t, test.expectedUser, brw.User) 140 }) 141 } 142 } 143 144 func TestScrubEids(t *testing.T) { 145 testCases := []struct { 146 name string 147 userIn *openrtb2.User 148 expectedUser *openrtb2.User 149 }{ 150 { 151 name: "eids", 152 userIn: &openrtb2.User{ID: "ID", EIDs: []openrtb2.EID{}}, 153 expectedUser: &openrtb2.User{ID: "ID", EIDs: nil}, 154 }, 155 { 156 name: "nil_eids", 157 userIn: &openrtb2.User{ID: "ID", EIDs: nil}, 158 expectedUser: &openrtb2.User{ID: "ID", EIDs: nil}, 159 }, 160 { 161 name: "nil", 162 userIn: nil, 163 expectedUser: nil, 164 }, 165 } 166 for _, test := range testCases { 167 t.Run(test.name, func(t *testing.T) { 168 brw := &openrtb_ext.RequestWrapper{BidRequest: &openrtb2.BidRequest{User: test.userIn}} 169 ScrubEIDs(brw) 170 brw.RebuildRequest() 171 assert.Equal(t, test.expectedUser, brw.User) 172 }) 173 } 174 } 175 176 func TestScrubTID(t *testing.T) { 177 testCases := []struct { 178 name string 179 sourceIn *openrtb2.Source 180 impIn []openrtb2.Imp 181 expectedSource *openrtb2.Source 182 expectedImp []openrtb2.Imp 183 }{ 184 { 185 name: "nil", 186 sourceIn: nil, 187 expectedSource: nil, 188 }, 189 { 190 name: "nil_imp_ext", 191 sourceIn: &openrtb2.Source{TID: "tid"}, 192 impIn: []openrtb2.Imp{{ID: "impID", Ext: nil}}, 193 expectedSource: &openrtb2.Source{TID: ""}, 194 expectedImp: []openrtb2.Imp{{ID: "impID", Ext: nil}}, 195 }, 196 { 197 name: "empty_imp_ext", 198 sourceIn: &openrtb2.Source{TID: "tid"}, 199 impIn: []openrtb2.Imp{{ID: "impID", Ext: json.RawMessage(`{}`)}}, 200 expectedSource: &openrtb2.Source{TID: ""}, 201 expectedImp: []openrtb2.Imp{{ID: "impID", Ext: json.RawMessage(`{}`)}}, 202 }, 203 { 204 name: "ext_with_tid", 205 sourceIn: &openrtb2.Source{TID: "tid"}, 206 impIn: []openrtb2.Imp{{ID: "impID", Ext: json.RawMessage(`{"tid":"123","test":1}`)}}, 207 expectedSource: &openrtb2.Source{TID: ""}, 208 expectedImp: []openrtb2.Imp{{ID: "impID", Ext: json.RawMessage(`{"test":1}`)}}, 209 }, 210 { 211 name: "ext_without_tid", 212 sourceIn: &openrtb2.Source{TID: "tid"}, 213 impIn: []openrtb2.Imp{{ID: "impID", Ext: json.RawMessage(`{"data":"123","test":1}`)}}, 214 expectedSource: &openrtb2.Source{TID: ""}, 215 expectedImp: []openrtb2.Imp{{ID: "impID", Ext: json.RawMessage(`{"data":"123","test":1}`)}}, 216 }, 217 } 218 for _, test := range testCases { 219 t.Run(test.name, func(t *testing.T) { 220 brw := &openrtb_ext.RequestWrapper{BidRequest: &openrtb2.BidRequest{Source: test.sourceIn, Imp: test.impIn}} 221 ScrubTID(brw) 222 brw.RebuildRequest() 223 assert.Equal(t, test.expectedSource, brw.Source) 224 assert.Equal(t, test.expectedImp, brw.Imp) 225 }) 226 } 227 } 228 229 func TestScrubGEO(t *testing.T) { 230 testCases := []struct { 231 name string 232 userIn *openrtb2.User 233 expectedUser *openrtb2.User 234 deviceIn *openrtb2.Device 235 expectedDevice *openrtb2.Device 236 }{ 237 { 238 name: "nil", 239 userIn: nil, 240 expectedUser: nil, 241 deviceIn: nil, 242 expectedDevice: nil, 243 }, 244 { 245 name: "nil_user_geo", 246 userIn: &openrtb2.User{ID: "ID", Geo: nil}, 247 expectedUser: &openrtb2.User{ID: "ID", Geo: nil}, 248 deviceIn: &openrtb2.Device{Geo: &openrtb2.Geo{Lat: ptrutil.ToPtr(123.123)}}, 249 expectedDevice: &openrtb2.Device{Geo: &openrtb2.Geo{Lat: ptrutil.ToPtr(123.12)}}, 250 }, 251 { 252 name: "with_user_geo", 253 userIn: &openrtb2.User{ID: "ID", Geo: &openrtb2.Geo{Lat: ptrutil.ToPtr(123.123)}}, 254 expectedUser: &openrtb2.User{ID: "ID", Geo: &openrtb2.Geo{Lat: ptrutil.ToPtr(123.12)}}, 255 deviceIn: &openrtb2.Device{}, 256 expectedDevice: &openrtb2.Device{}, 257 }, 258 { 259 name: "nil_device_geo", 260 userIn: &openrtb2.User{}, 261 expectedUser: &openrtb2.User{}, 262 deviceIn: &openrtb2.Device{Geo: nil}, 263 expectedDevice: &openrtb2.Device{Geo: nil}, 264 }, 265 { 266 name: "with_device_geo", 267 userIn: &openrtb2.User{}, 268 expectedUser: &openrtb2.User{}, 269 deviceIn: &openrtb2.Device{Geo: &openrtb2.Geo{Lat: ptrutil.ToPtr(123.123)}}, 270 expectedDevice: &openrtb2.Device{Geo: &openrtb2.Geo{Lat: ptrutil.ToPtr(123.12)}}, 271 }, 272 { 273 name: "with_user_and_device_geo", 274 userIn: &openrtb2.User{ID: "ID", Geo: &openrtb2.Geo{Lat: ptrutil.ToPtr(123.123)}}, 275 expectedUser: &openrtb2.User{ID: "ID", Geo: &openrtb2.Geo{Lat: ptrutil.ToPtr(123.12)}}, 276 deviceIn: &openrtb2.Device{Geo: &openrtb2.Geo{Lat: ptrutil.ToPtr(123.123)}}, 277 expectedDevice: &openrtb2.Device{Geo: &openrtb2.Geo{Lat: ptrutil.ToPtr(123.12)}}, 278 }, 279 } 280 for _, test := range testCases { 281 t.Run(test.name, func(t *testing.T) { 282 brw := &openrtb_ext.RequestWrapper{BidRequest: &openrtb2.BidRequest{User: test.userIn, Device: test.deviceIn}} 283 scrubGEO(brw) 284 brw.RebuildRequest() 285 assert.Equal(t, test.expectedUser, brw.User) 286 assert.Equal(t, test.expectedDevice, brw.Device) 287 }) 288 } 289 } 290 291 func TestScrubGeoFull(t *testing.T) { 292 testCases := []struct { 293 name string 294 userIn *openrtb2.User 295 expectedUser *openrtb2.User 296 deviceIn *openrtb2.Device 297 expectedDevice *openrtb2.Device 298 }{ 299 { 300 name: "nil", 301 userIn: nil, 302 expectedUser: nil, 303 deviceIn: nil, 304 expectedDevice: nil, 305 }, 306 { 307 name: "nil_user_geo", 308 userIn: &openrtb2.User{ID: "ID", Geo: nil}, 309 expectedUser: &openrtb2.User{ID: "ID", Geo: nil}, 310 deviceIn: &openrtb2.Device{}, 311 expectedDevice: &openrtb2.Device{}, 312 }, 313 { 314 name: "with_user_geo", 315 userIn: &openrtb2.User{ID: "ID", Geo: &openrtb2.Geo{Lat: ptrutil.ToPtr(123.123)}}, 316 expectedUser: &openrtb2.User{ID: "ID", Geo: &openrtb2.Geo{}}, 317 deviceIn: &openrtb2.Device{}, 318 expectedDevice: &openrtb2.Device{}, 319 }, 320 { 321 name: "nil_device_geo", 322 userIn: &openrtb2.User{}, 323 expectedUser: &openrtb2.User{}, 324 deviceIn: &openrtb2.Device{Geo: nil}, 325 expectedDevice: &openrtb2.Device{Geo: nil}, 326 }, 327 { 328 name: "with_device_geo", 329 userIn: &openrtb2.User{}, 330 expectedUser: &openrtb2.User{}, 331 deviceIn: &openrtb2.Device{Geo: &openrtb2.Geo{Lat: ptrutil.ToPtr(123.123)}}, 332 expectedDevice: &openrtb2.Device{Geo: &openrtb2.Geo{}}, 333 }, 334 { 335 name: "with_user_and_device_geo", 336 userIn: &openrtb2.User{ID: "ID", Geo: &openrtb2.Geo{Lat: ptrutil.ToPtr(123.123)}}, 337 expectedUser: &openrtb2.User{ID: "ID", Geo: &openrtb2.Geo{}}, 338 deviceIn: &openrtb2.Device{Geo: &openrtb2.Geo{Lat: ptrutil.ToPtr(123.123)}}, 339 expectedDevice: &openrtb2.Device{Geo: &openrtb2.Geo{}}, 340 }, 341 } 342 for _, test := range testCases { 343 t.Run(test.name, func(t *testing.T) { 344 brw := &openrtb_ext.RequestWrapper{BidRequest: &openrtb2.BidRequest{User: test.userIn, Device: test.deviceIn}} 345 scrubGeoFull(brw) 346 brw.RebuildRequest() 347 assert.Equal(t, test.expectedUser, brw.User) 348 assert.Equal(t, test.expectedDevice, brw.Device) 349 }) 350 } 351 } 352 353 func TestScrubIP(t *testing.T) { 354 testCases := []struct { 355 IP string 356 cleanedIP string 357 bits int 358 maskBits int 359 }{ 360 { 361 IP: "0:0:0:0:0:0:0:0", 362 cleanedIP: "::", 363 bits: 128, 364 maskBits: 56, 365 }, 366 { 367 IP: "", 368 cleanedIP: "", 369 bits: 128, 370 maskBits: 56, 371 }, 372 { 373 IP: "1111:2222:3333:4444:5555:6666:7777:8888", 374 cleanedIP: "1111:2222:3333:4400::", 375 bits: 128, 376 maskBits: 56, 377 }, 378 { 379 IP: "1111:2222:3333:4444:5555:6666:7777:8888", 380 cleanedIP: "1111:2222::", 381 bits: 128, 382 maskBits: 34, 383 }, 384 { 385 IP: "1111:0:3333:4444:5555:6666:7777:8888", 386 cleanedIP: "1111:0:3333:4400::", 387 bits: 128, 388 maskBits: 56, 389 }, 390 { 391 IP: "1111::6666:7777:8888", 392 cleanedIP: "1111::", 393 bits: 128, 394 maskBits: 56, 395 }, 396 { 397 IP: "2001:1db8:0000:0000:0000:ff00:0042:8329", 398 cleanedIP: "2001:1db8::ff00:0:0", 399 bits: 128, 400 maskBits: 96, 401 }, 402 { 403 IP: "2001:1db8:0000:0000:0000:ff00:0:0", 404 cleanedIP: "2001:1db8::ff00:0:0", 405 bits: 128, 406 maskBits: 96, 407 }, 408 } 409 for _, test := range testCases { 410 t.Run(test.IP, func(t *testing.T) { 411 // bits: ipv6 - 128, ipv4 - 32 412 result := scrubIP(test.IP, test.maskBits, test.bits) 413 assert.Equal(t, test.cleanedIP, result) 414 }) 415 } 416 } 417 418 func TestScrubGeoPrecision(t *testing.T) { 419 geo := &openrtb2.Geo{ 420 Lat: ptrutil.ToPtr(123.456), 421 Lon: ptrutil.ToPtr(678.89), 422 Metro: "some metro", 423 City: "some city", 424 ZIP: "some zip", 425 } 426 geoExpected := &openrtb2.Geo{ 427 Lat: ptrutil.ToPtr(123.46), 428 Lon: ptrutil.ToPtr(678.89), 429 Metro: "some metro", 430 City: "some city", 431 ZIP: "some zip", 432 } 433 434 result := scrubGeoPrecision(geo) 435 436 assert.Equal(t, geoExpected, result) 437 } 438 439 func TestScrubGeoPrecisionWhenNil(t *testing.T) { 440 result := scrubGeoPrecision(nil) 441 assert.Nil(t, result) 442 } 443 444 func TestScrubUserExtIDs(t *testing.T) { 445 testCases := []struct { 446 description string 447 userExt json.RawMessage 448 expected json.RawMessage 449 }{ 450 { 451 description: "Nil", 452 userExt: nil, 453 expected: nil, 454 }, 455 { 456 description: "Empty String", 457 userExt: json.RawMessage(``), 458 expected: json.RawMessage(``), 459 }, 460 { 461 description: "Empty Object", 462 userExt: json.RawMessage(`{}`), 463 expected: json.RawMessage(`{}`), 464 }, 465 { 466 description: "Do Nothing When Malformed", 467 userExt: json.RawMessage(`malformed`), 468 expected: json.RawMessage(`malformed`), 469 }, 470 { 471 description: "Do Nothing When No IDs Present", 472 userExt: json.RawMessage(`{"anyExisting":42}}`), 473 expected: json.RawMessage(`{"anyExisting":42}}`), 474 }, 475 { 476 description: "Remove eids", 477 userExt: json.RawMessage(`{"eids":[{"source":"anySource","id":"anyId","uids":[{"id":"anyId","ext":{"id":42}}],"ext":{"id":42}}]}`), 478 expected: json.RawMessage(`{}`), 479 }, 480 { 481 description: "Remove eids - With Other Data", 482 userExt: json.RawMessage(`{"anyExisting":42,"eids":[{"source":"anySource","id":"anyId","uids":[{"id":"anyId","ext":{"id":42}}],"ext":{"id":42}}]}`), 483 expected: json.RawMessage(`{"anyExisting":42}`), 484 }, 485 { 486 description: "Remove eids - With Other Nested Data", 487 userExt: json.RawMessage(`{"anyExisting":{"existing":42},"eids":[{"source":"anySource","id":"anyId","uids":[{"id":"anyId","ext":{"id":42}}],"ext":{"id":42}}]}`), 488 expected: json.RawMessage(`{"anyExisting":{"existing":42}}`), 489 }, 490 { 491 description: "Remove eids Only - Empty Array", 492 userExt: json.RawMessage(`{"eids":[]}`), 493 expected: json.RawMessage(`{}`), 494 }, 495 } 496 497 for _, test := range testCases { 498 result := scrubExtIDs(test.userExt, "eids") 499 assert.Equal(t, test.expected, result, test.description) 500 } 501 }