Skip to content

Commit fa7c2ea

Browse files
CTMBNaraRitesh Ghodrao
authored andcommitted
Core: Pad GPP consent string sections (prebid#3921)
1 parent 95f21b0 commit fa7c2ea

7 files changed

Lines changed: 375 additions & 9 deletions

File tree

src/main/java/org/prebid/server/auction/gpp/model/GppModelWrapper.java

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,33 @@ public class GppModelWrapper extends GppModel {
1717
private IntObjectMap<String> sectionIdToEncodedString;
1818

1919
public GppModelWrapper(String encodedString) throws DecodingException {
20-
super(encodedString);
20+
super(padSections(encodedString));
21+
}
22+
23+
private static String padSections(String gpp) {
24+
final StringBuilder gppBuilder = new StringBuilder(gpp);
25+
26+
int subsectionStart = 0;
27+
int offset = 0;
28+
for (int i = 1; i < gpp.length(); i++) {
29+
final char currentChar = gpp.charAt(i);
30+
31+
if (currentChar == '~' || currentChar == '.') {
32+
if ((i - subsectionStart) % 4 != 0 && gpp.charAt(i - 1) != '=') {
33+
gppBuilder.insert(i + offset, "A");
34+
offset++;
35+
}
36+
37+
subsectionStart = i + 1;
38+
}
39+
}
40+
41+
final int lastSubsectionLength = gpp.length() - subsectionStart;
42+
if (lastSubsectionLength > 0 && lastSubsectionLength % 4 != 0 && !gpp.endsWith("=")) {
43+
gppBuilder.append("A");
44+
}
45+
46+
return gppBuilder.toString();
2147
}
2248

2349
private void init() {

src/test/groovy/org/prebid/server/functional/tests/privacy/GppSyncUserActivitiesSpec.groovy

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -483,6 +483,39 @@ class GppSyncUserActivitiesSpec extends PrivacyBaseSpec {
483483
]
484484
}
485485

486+
def "PBS cookie sync call when privacy module contain invalid GPP string should exclude bidders URLs"() {
487+
given: "Cookie sync request with link to account"
488+
def accountId = PBSUtils.randomString
489+
def cookieSyncRequest = CookieSyncRequest.defaultCookieSyncRequest.tap {
490+
it.gppSid = US_NAT_V1.value
491+
it.account = accountId
492+
it.gpp = INVALID_GPP_STRING
493+
}
494+
495+
and: "Activities set for cookie sync with allowing privacy regulation"
496+
def rule = new ActivityRule().tap {
497+
it.privacyRegulation = [IAB_US_GENERAL]
498+
}
499+
500+
def activities = AllowActivities.getDefaultAllowActivities(SYNC_USER, Activity.getDefaultActivity([rule]))
501+
502+
and: "Account gpp configuration"
503+
def accountGppConfig = new AccountGppConfig(code: IAB_US_GENERAL, enabled: true)
504+
505+
and: "Existed account with cookie sync and privacy regulation setup"
506+
def account = getAccountWithAllowActivitiesAndPrivacyModule(accountId, activities, [accountGppConfig])
507+
accountDao.save(account)
508+
509+
when: "PBS processes cookie sync request"
510+
def response = activityPbsService.sendCookieSyncRequest(cookieSyncRequest)
511+
512+
then: "Response should not contain any URLs for bidders"
513+
assert !response.bidderStatus.userSync.url
514+
515+
and: "Response should not contain any warning"
516+
assert !response.warnings
517+
}
518+
486519
def "PBS cookie sync call when request have different gpp consent but match and rejecting should exclude bidders URLs"() {
487520
given: "Cookie sync request with link to account"
488521
def accountId = PBSUtils.randomString
@@ -1326,6 +1359,41 @@ class GppSyncUserActivitiesSpec extends PrivacyBaseSpec {
13261359
]
13271360
}
13281361

1362+
def "PBS setuid request when privacy module contain invalid GPP string should reject bidders with status code invalidStatusCode"() {
1363+
given: "Cookie sync SetuidRequest with accountId"
1364+
def accountId = PBSUtils.randomString
1365+
def setuidRequest = SetuidRequest.defaultSetuidRequest.tap {
1366+
it.account = accountId
1367+
it.gppSid = US_NAT_V1.value
1368+
it.gpp = INVALID_GPP_STRING
1369+
}
1370+
1371+
and: "UIDS Cookie"
1372+
def uidsCookie = UidsCookie.defaultUidsCookie
1373+
1374+
and: "Activities set for cookie sync with allowing privacy regulation"
1375+
def rule = new ActivityRule().tap {
1376+
it.privacyRegulation = [IAB_US_GENERAL]
1377+
}
1378+
1379+
def activities = AllowActivities.getDefaultAllowActivities(SYNC_USER, Activity.getDefaultActivity([rule]))
1380+
1381+
and: "Account gpp configuration"
1382+
def accountGppConfig = new AccountGppConfig(code: IAB_US_GENERAL, enabled: true)
1383+
1384+
and: "Existed account with cookie sync and allow activities setup"
1385+
def account = getAccountWithAllowActivitiesAndPrivacyModule(accountId, activities, [accountGppConfig])
1386+
accountDao.save(account)
1387+
1388+
when: "PBS processes cookie sync request"
1389+
activityPbsService.sendSetUidRequest(setuidRequest, uidsCookie)
1390+
1391+
then: "Request should fail with error"
1392+
def exception = thrown(PrebidServerException)
1393+
assert exception.statusCode == INVALID_STATUS_CODE
1394+
assert exception.responseBody == INVALID_STATUS_MESSAGE
1395+
}
1396+
13291397
def "PBS setuid request when request have different gpp consent but match and rejecting should reject bidders with status code invalidStatusCode"() {
13301398
given: "Cookie sync SetuidRequest with accountId"
13311399
def accountId = PBSUtils.randomString

src/test/groovy/org/prebid/server/functional/tests/privacy/GppTransmitEidsActivitiesSpec.groovy

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ import static org.prebid.server.functional.model.request.auction.PrivacyModule.I
7777
import static org.prebid.server.functional.model.request.auction.PrivacyModule.IAB_US_CUSTOM_LOGIC
7878
import static org.prebid.server.functional.model.request.auction.PrivacyModule.IAB_US_GENERAL
7979
import static org.prebid.server.functional.model.request.auction.TraceLevel.VERBOSE
80+
import static org.prebid.server.functional.model.response.auction.ErrorType.PREBID
8081
import static org.prebid.server.functional.util.privacy.model.State.ALABAMA
8182
import static org.prebid.server.functional.util.privacy.model.State.ONTARIO
8283

@@ -842,6 +843,45 @@ class GppTransmitEidsActivitiesSpec extends PrivacyBaseSpec {
842843
]
843844
}
844845

846+
def "PBS auction call when privacy module contain invalid GPP string should remove EIDS fields in request"() {
847+
given: "Default Generic BidRequests with EIDS fields and account id"
848+
def accountId = PBSUtils.randomNumber as String
849+
def bidRequest = getBidRequestWithPersonalData(accountId).tap {
850+
regs.gppSid = [US_NAT_V1.intValue]
851+
regs.gpp = INVALID_GPP_STRING
852+
}
853+
854+
and: "Activities set for transmitEIDS with rejecting privacy regulation"
855+
def rule = new ActivityRule().tap {
856+
it.privacyRegulation = [IAB_US_GENERAL]
857+
}
858+
859+
def activities = AllowActivities.getDefaultAllowActivities(TRANSMIT_EIDS, Activity.getDefaultActivity([rule]))
860+
861+
and: "Account gpp configuration"
862+
def accountGppConfig = new AccountGppConfig(code: IAB_US_GENERAL, enabled: true)
863+
864+
and: "Existed account with privacy regulation setup"
865+
def account = getAccountWithAllowActivitiesAndPrivacyModule(accountId, activities, [accountGppConfig])
866+
accountDao.save(account)
867+
868+
when: "PBS processes auction requests"
869+
def response = activityPbsService.sendAuctionRequest(bidRequest)
870+
871+
then: "Generic bidder request should have empty EIDS fields"
872+
def genericBidderRequest = bidder.getBidderRequest(bidRequest.id)
873+
verifyAll {
874+
!genericBidderRequest.user.eids
875+
!genericBidderRequest.user?.ext?.eids
876+
}
877+
878+
and: "Response should not contain any warnings"
879+
assert !response.ext.warnings
880+
881+
and: "Response should not contain any errors"
882+
assert !response.ext.errors
883+
}
884+
845885
def "PBS auction call when request have different gpp consent but match and rejecting should remove EIDS fields in request"() {
846886
given: "Default Generic BidRequests with EIDS fields and account id"
847887
def accountId = PBSUtils.randomNumber as String
@@ -1830,6 +1870,55 @@ class GppTransmitEidsActivitiesSpec extends PrivacyBaseSpec {
18301870
]
18311871
}
18321872

1873+
def "PBS amp call when privacy module contain invalid GPP string should remove EIDS fields in request"() {
1874+
given: "Default Generic BidRequest with EIDS fields field and account id"
1875+
def accountId = PBSUtils.randomNumber as String
1876+
def ampStoredRequest = getBidRequestWithPersonalData(accountId)
1877+
1878+
and: "amp request with link to account"
1879+
def ampRequest = AmpRequest.defaultAmpRequest.tap {
1880+
it.account = accountId
1881+
it.gppSid = US_NAT_V1.value
1882+
it.consentString = INVALID_GPP_STRING
1883+
it.consentType = GPP
1884+
}
1885+
1886+
and: "Activities set for transmitEIDS with allowing privacy regulation"
1887+
def rule = new ActivityRule().tap {
1888+
it.privacyRegulation = [IAB_US_GENERAL]
1889+
}
1890+
1891+
def activities = AllowActivities.getDefaultAllowActivities(TRANSMIT_EIDS, Activity.getDefaultActivity([rule]))
1892+
1893+
and: "Account gpp configuration"
1894+
def accountGppConfig = new AccountGppConfig(code: IAB_US_GENERAL, enabled: true)
1895+
1896+
and: "Existed account with privacy regulation setup"
1897+
def account = getAccountWithAllowActivitiesAndPrivacyModule(accountId, activities, [accountGppConfig])
1898+
accountDao.save(account)
1899+
1900+
and: "Stored request in DB"
1901+
def storedRequest = StoredRequest.getStoredRequest(ampRequest, ampStoredRequest)
1902+
storedRequestDao.save(storedRequest)
1903+
1904+
when: "PBS processes amp request"
1905+
def response = activityPbsService.sendAmpRequest(ampRequest)
1906+
1907+
then: "Generic bidder request should have empty EIDS fields"
1908+
def genericBidderRequest = bidder.getBidderRequest(ampStoredRequest.id)
1909+
verifyAll {
1910+
!genericBidderRequest.user.eids
1911+
!genericBidderRequest.user?.ext?.eids
1912+
}
1913+
1914+
and: "Response should not contain any warnings"
1915+
assert !response.ext.warnings
1916+
1917+
and: "Response should contain amp error"
1918+
assert response.ext?.errors[PREBID]*.code == [999]
1919+
assert response.ext?.errors[PREBID]*.message == ["Amp request parameter consent_string has invalid format: $INVALID_GPP_STRING"]
1920+
}
1921+
18331922
def "PBS amp call when request have different gpp consent but match and rejecting should remove EIDS fields in request"() {
18341923
given: "Default Generic BidRequest with EIDS fields field and account id"
18351924
def accountId = PBSUtils.randomNumber as String

src/test/groovy/org/prebid/server/functional/tests/privacy/GppTransmitUfpdActivitiesSpec.groovy

Lines changed: 117 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,16 +15,10 @@ import org.prebid.server.functional.model.request.amp.AmpRequest
1515
import org.prebid.server.functional.model.request.auction.Activity
1616
import org.prebid.server.functional.model.request.auction.ActivityRule
1717
import org.prebid.server.functional.model.request.auction.AllowActivities
18-
import org.prebid.server.functional.model.request.auction.BidRequest
1918
import org.prebid.server.functional.model.request.auction.Condition
20-
import org.prebid.server.functional.model.request.auction.Data
2119
import org.prebid.server.functional.model.request.auction.Device
22-
import org.prebid.server.functional.model.request.auction.Eid
2320
import org.prebid.server.functional.model.request.auction.Geo
2421
import org.prebid.server.functional.model.request.auction.RegsExt
25-
import org.prebid.server.functional.model.request.auction.User
26-
import org.prebid.server.functional.model.request.auction.UserExt
27-
import org.prebid.server.functional.model.request.auction.UserExtData
2822
import org.prebid.server.functional.service.PrebidServerException
2923
import org.prebid.server.functional.util.PBSUtils
3024
import org.prebid.server.functional.util.privacy.gpp.UsCaV1Consent
@@ -87,6 +81,7 @@ import static org.prebid.server.functional.model.request.auction.PrivacyModule.I
8781
import static org.prebid.server.functional.model.request.auction.PrivacyModule.IAB_US_CUSTOM_LOGIC
8882
import static org.prebid.server.functional.model.request.auction.PrivacyModule.IAB_US_GENERAL
8983
import static org.prebid.server.functional.model.request.auction.TraceLevel.VERBOSE
84+
import static org.prebid.server.functional.model.response.auction.ErrorType.PREBID
9085
import static org.prebid.server.functional.util.privacy.model.State.ALABAMA
9186
import static org.prebid.server.functional.util.privacy.model.State.ONTARIO
9287

@@ -1114,6 +1109,59 @@ class GppTransmitUfpdActivitiesSpec extends PrivacyBaseSpec {
11141109
]
11151110
}
11161111

1112+
def "PBS auction call when privacy module contain invalid GPP string should remove UFPD fields in request"() {
1113+
given: "Default Generic BidRequests with UFPD fields and account id"
1114+
def accountId = PBSUtils.randomNumber as String
1115+
def bidRequest = getBidRequestWithPersonalData(accountId).tap {
1116+
regs.gppSid = [US_NAT_V1.intValue]
1117+
regs.gpp = INVALID_GPP_STRING
1118+
}
1119+
1120+
and: "Activities set for transmitUfpd with rejecting privacy regulation"
1121+
def rule = new ActivityRule().tap {
1122+
it.privacyRegulation = [IAB_US_GENERAL]
1123+
}
1124+
1125+
def activities = AllowActivities.getDefaultAllowActivities(TRANSMIT_UFPD, Activity.getDefaultActivity([rule]))
1126+
1127+
and: "Account gpp configuration"
1128+
def accountGppConfig = new AccountGppConfig(code: IAB_US_GENERAL, enabled: true)
1129+
1130+
and: "Existed account with privacy regulation setup"
1131+
def account = getAccountWithAllowActivitiesAndPrivacyModule(accountId, activities, [accountGppConfig])
1132+
accountDao.save(account)
1133+
1134+
when: "PBS processes auction requests"
1135+
def response= activityPbsService.sendAuctionRequest(bidRequest)
1136+
1137+
then: "Generic bidder request should have empty UFPD fields"
1138+
def bidderRequest = bidder.getBidderRequest(bidRequest.id)
1139+
verifyAll {
1140+
!bidderRequest.device.didsha1
1141+
!bidderRequest.device.didmd5
1142+
!bidderRequest.device.dpidsha1
1143+
!bidderRequest.device.ifa
1144+
!bidderRequest.device.macsha1
1145+
!bidderRequest.device.macmd5
1146+
!bidderRequest.device.dpidmd5
1147+
!bidderRequest.user.id
1148+
!bidderRequest.user.buyeruid
1149+
!bidderRequest.user.yob
1150+
!bidderRequest.user.gender
1151+
!bidderRequest.user.data
1152+
!bidderRequest.user.ext
1153+
}
1154+
1155+
and: "Generic bidder request should have data in EIDS fields"
1156+
assert bidderRequest.user.eids == bidRequest.user.eids
1157+
1158+
and: "Response should not contain any warnings"
1159+
assert !response.ext.warnings
1160+
1161+
and: "Response should not contain any errors"
1162+
assert !response.ext.errors
1163+
}
1164+
11171165
def "PBS auction call when request have different gpp consent but match and rejecting should remove UFPD fields in request"() {
11181166
given: "Default Generic BidRequests with UFPD fields and account id"
11191167
def accountId = PBSUtils.randomNumber as String
@@ -2387,6 +2435,69 @@ class GppTransmitUfpdActivitiesSpec extends PrivacyBaseSpec {
23872435
]
23882436
}
23892437

2438+
def "PBS amp call when privacy module contain invalid GPP string should remove UFPD fields in request"() {
2439+
given: "Default Generic BidRequest with UFPD fields field and account id"
2440+
def accountId = PBSUtils.randomNumber as String
2441+
def ampStoredRequest = getBidRequestWithPersonalData(accountId)
2442+
2443+
and: "amp request with link to account"
2444+
def ampRequest = AmpRequest.defaultAmpRequest.tap {
2445+
it.account = accountId
2446+
it.gppSid = US_NAT_V1.value
2447+
it.consentString = INVALID_GPP_STRING
2448+
it.consentType = GPP
2449+
}
2450+
2451+
and: "Activities set for transmitUfpd with allowing privacy regulation"
2452+
def rule = new ActivityRule().tap {
2453+
it.privacyRegulation = [IAB_US_GENERAL]
2454+
}
2455+
2456+
def activities = AllowActivities.getDefaultAllowActivities(TRANSMIT_UFPD, Activity.getDefaultActivity([rule]))
2457+
2458+
and: "Account gpp configuration"
2459+
def accountGppConfig = new AccountGppConfig(code: IAB_US_GENERAL, enabled: true)
2460+
2461+
and: "Existed account with privacy regulation setup"
2462+
def account = getAccountWithAllowActivitiesAndPrivacyModule(accountId, activities, [accountGppConfig])
2463+
accountDao.save(account)
2464+
2465+
and: "Stored request in DB"
2466+
def storedRequest = StoredRequest.getStoredRequest(ampRequest, ampStoredRequest)
2467+
storedRequestDao.save(storedRequest)
2468+
2469+
when: "PBS processes amp request"
2470+
def response = activityPbsService.sendAmpRequest(ampRequest)
2471+
2472+
then: "Generic bidder request should have empty UFPD fields"
2473+
def bidderRequest = bidder.getBidderRequest(ampStoredRequest.id)
2474+
verifyAll {
2475+
!bidderRequest.device.didsha1
2476+
!bidderRequest.device.didmd5
2477+
!bidderRequest.device.dpidsha1
2478+
!bidderRequest.device.ifa
2479+
!bidderRequest.device.macsha1
2480+
!bidderRequest.device.macmd5
2481+
!bidderRequest.device.dpidmd5
2482+
!bidderRequest.user.id
2483+
!bidderRequest.user.buyeruid
2484+
!bidderRequest.user.yob
2485+
!bidderRequest.user.gender
2486+
!bidderRequest.user.data
2487+
!bidderRequest.user.ext
2488+
}
2489+
2490+
and: "Generic bidder request should have data in EIDS fields"
2491+
assert bidderRequest.user.eids == ampStoredRequest.user.eids
2492+
2493+
and: "Response should not contain any warnings"
2494+
assert !response.ext.warnings
2495+
2496+
and: "Response should contain amp error"
2497+
assert response.ext?.errors[PREBID]*.code == [999]
2498+
assert response.ext?.errors[PREBID]*.message == ["Amp request parameter consent_string has invalid format: $INVALID_GPP_STRING"]
2499+
}
2500+
23902501
def "PBS amp call when request have different gpp consent but match and rejecting should remove UFPD fields in request"() {
23912502
given: "Default Generic BidRequest with UFPD fields field and account id"
23922503
def accountId = PBSUtils.randomNumber as String

src/test/groovy/org/prebid/server/functional/tests/privacy/PrivacyBaseSpec.groovy

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ abstract class PrivacyBaseSpec extends BaseSpec {
9595
private static final Map<String, String> GDPR_EEA_COUNTRY = ["gdpr.eea-countries": "$BULGARIA.ISOAlpha2, SK, VK" as String]
9696

9797
protected static final String VENDOR_LIST_PATH = "/app/prebid-server/data/vendorlist-v{VendorVersion}/{VendorVersion}.json"
98+
protected static final String INVALID_GPP_STRING = "DBABLA~BVQqAAAAAg.YA" // TODO replace BVQqAAAAAg with ${PBSUtils.getRandomString(7)} when proper fix is ready
9899
protected static final String VALID_VALUE_FOR_GPC_HEADER = "1"
99100
protected static final GppConsent SIMPLE_GPC_DISALLOW_LOGIC = new UsNatV1Consent.Builder().setGpc(true).build()
100101
protected static final VendorList vendorListResponse = new VendorList(networkServiceContainer)

src/test/java/org/prebid/server/auction/gpp/model/GppContextCreatorTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ public void fromShouldReturnGppContextWrapperWithErrorOnInvalidGpp() {
3737
assertThat(gppContext.regions()).isEqualTo(GppContext.Regions.builder().build());
3838
});
3939
assertThat(gppContextWrapper.getErrors())
40-
.containsExactly("GPP string invalid: Unable to decode 'invalid'");
40+
.containsExactly("GPP string invalid: Unable to decode 'invalidA'");
4141
}
4242

4343
@Test

0 commit comments

Comments
 (0)