Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,33 @@ public class GppModelWrapper extends GppModel {
private IntObjectMap<String> sectionIdToEncodedString;

public GppModelWrapper(String encodedString) throws DecodingException {
super(encodedString);
super(padSections(encodedString));
}

private static String padSections(String gpp) {
final StringBuilder gppBuilder = new StringBuilder(gpp);

int subsectionStart = 0;
int offset = 0;
for (int i = 1; i < gpp.length(); i++) {
final char currentChar = gpp.charAt(i);

if (currentChar == '~' || currentChar == '.') {
if ((i - subsectionStart) % 4 != 0 && gpp.charAt(i - 1) != '=') {
gppBuilder.insert(i + offset, "A");
offset++;
}

subsectionStart = i + 1;
}
}

final int lastSubsectionLength = gpp.length() - subsectionStart;
if (lastSubsectionLength > 0 && lastSubsectionLength % 4 != 0 && !gpp.endsWith("=")) {
gppBuilder.append("A");
}

return gppBuilder.toString();
}

private void init() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -483,6 +483,39 @@ class GppSyncUserActivitiesSpec extends PrivacyBaseSpec {
]
}

def "PBS cookie sync call when privacy module contain invalid GPP string should exclude bidders URLs"() {
given: "Cookie sync request with link to account"
def accountId = PBSUtils.randomString
def cookieSyncRequest = CookieSyncRequest.defaultCookieSyncRequest.tap {
it.gppSid = US_NAT_V1.value
it.account = accountId
it.gpp = INVALID_GPP_STRING
}

and: "Activities set for cookie sync with allowing privacy regulation"
def rule = new ActivityRule().tap {
it.privacyRegulation = [IAB_US_GENERAL]
}

def activities = AllowActivities.getDefaultAllowActivities(SYNC_USER, Activity.getDefaultActivity([rule]))

and: "Account gpp configuration"
def accountGppConfig = new AccountGppConfig(code: IAB_US_GENERAL, enabled: true)

and: "Existed account with cookie sync and privacy regulation setup"
def account = getAccountWithAllowActivitiesAndPrivacyModule(accountId, activities, [accountGppConfig])
accountDao.save(account)

when: "PBS processes cookie sync request"
def response = activityPbsService.sendCookieSyncRequest(cookieSyncRequest)

then: "Response should not contain any URLs for bidders"
assert !response.bidderStatus.userSync.url

and: "Response should not contain any warning"
assert !response.warnings
}

def "PBS cookie sync call when request have different gpp consent but match and rejecting should exclude bidders URLs"() {
given: "Cookie sync request with link to account"
def accountId = PBSUtils.randomString
Expand Down Expand Up @@ -1326,6 +1359,41 @@ class GppSyncUserActivitiesSpec extends PrivacyBaseSpec {
]
}

def "PBS setuid request when privacy module contain invalid GPP string should reject bidders with status code invalidStatusCode"() {
given: "Cookie sync SetuidRequest with accountId"
def accountId = PBSUtils.randomString
def setuidRequest = SetuidRequest.defaultSetuidRequest.tap {
it.account = accountId
it.gppSid = US_NAT_V1.value
it.gpp = INVALID_GPP_STRING
}

and: "UIDS Cookie"
def uidsCookie = UidsCookie.defaultUidsCookie

and: "Activities set for cookie sync with allowing privacy regulation"
def rule = new ActivityRule().tap {
it.privacyRegulation = [IAB_US_GENERAL]
}

def activities = AllowActivities.getDefaultAllowActivities(SYNC_USER, Activity.getDefaultActivity([rule]))

and: "Account gpp configuration"
def accountGppConfig = new AccountGppConfig(code: IAB_US_GENERAL, enabled: true)

and: "Existed account with cookie sync and allow activities setup"
def account = getAccountWithAllowActivitiesAndPrivacyModule(accountId, activities, [accountGppConfig])
accountDao.save(account)

when: "PBS processes cookie sync request"
activityPbsService.sendSetUidRequest(setuidRequest, uidsCookie)

then: "Request should fail with error"
def exception = thrown(PrebidServerException)
assert exception.statusCode == INVALID_STATUS_CODE
assert exception.responseBody == INVALID_STATUS_MESSAGE
}

def "PBS setuid request when request have different gpp consent but match and rejecting should reject bidders with status code invalidStatusCode"() {
given: "Cookie sync SetuidRequest with accountId"
def accountId = PBSUtils.randomString
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ import static org.prebid.server.functional.model.request.auction.PrivacyModule.I
import static org.prebid.server.functional.model.request.auction.PrivacyModule.IAB_US_CUSTOM_LOGIC
import static org.prebid.server.functional.model.request.auction.PrivacyModule.IAB_US_GENERAL
import static org.prebid.server.functional.model.request.auction.TraceLevel.VERBOSE
import static org.prebid.server.functional.model.response.auction.ErrorType.PREBID
import static org.prebid.server.functional.util.privacy.model.State.ALABAMA
import static org.prebid.server.functional.util.privacy.model.State.ONTARIO

Expand Down Expand Up @@ -842,6 +843,45 @@ class GppTransmitEidsActivitiesSpec extends PrivacyBaseSpec {
]
}

def "PBS auction call when privacy module contain invalid GPP string should remove EIDS fields in request"() {
given: "Default Generic BidRequests with EIDS fields and account id"
def accountId = PBSUtils.randomNumber as String
def bidRequest = getBidRequestWithPersonalData(accountId).tap {
regs.gppSid = [US_NAT_V1.intValue]
regs.gpp = INVALID_GPP_STRING
}

and: "Activities set for transmitEIDS with rejecting privacy regulation"
def rule = new ActivityRule().tap {
it.privacyRegulation = [IAB_US_GENERAL]
}

def activities = AllowActivities.getDefaultAllowActivities(TRANSMIT_EIDS, Activity.getDefaultActivity([rule]))

and: "Account gpp configuration"
def accountGppConfig = new AccountGppConfig(code: IAB_US_GENERAL, enabled: true)

and: "Existed account with privacy regulation setup"
def account = getAccountWithAllowActivitiesAndPrivacyModule(accountId, activities, [accountGppConfig])
accountDao.save(account)

when: "PBS processes auction requests"
def response = activityPbsService.sendAuctionRequest(bidRequest)

then: "Generic bidder request should have empty EIDS fields"
def genericBidderRequest = bidder.getBidderRequest(bidRequest.id)
verifyAll {
!genericBidderRequest.user.eids
!genericBidderRequest.user?.ext?.eids
}

and: "Response should not contain any warnings"
assert !response.ext.warnings

and: "Response should not contain any errors"
assert !response.ext.errors
}

def "PBS auction call when request have different gpp consent but match and rejecting should remove EIDS fields in request"() {
given: "Default Generic BidRequests with EIDS fields and account id"
def accountId = PBSUtils.randomNumber as String
Expand Down Expand Up @@ -1830,6 +1870,55 @@ class GppTransmitEidsActivitiesSpec extends PrivacyBaseSpec {
]
}

def "PBS amp call when privacy module contain invalid GPP string should remove EIDS fields in request"() {
given: "Default Generic BidRequest with EIDS fields field and account id"
def accountId = PBSUtils.randomNumber as String
def ampStoredRequest = getBidRequestWithPersonalData(accountId)

and: "amp request with link to account"
def ampRequest = AmpRequest.defaultAmpRequest.tap {
it.account = accountId
it.gppSid = US_NAT_V1.value
it.consentString = INVALID_GPP_STRING
it.consentType = GPP
}

and: "Activities set for transmitEIDS with allowing privacy regulation"
def rule = new ActivityRule().tap {
it.privacyRegulation = [IAB_US_GENERAL]
}

def activities = AllowActivities.getDefaultAllowActivities(TRANSMIT_EIDS, Activity.getDefaultActivity([rule]))

and: "Account gpp configuration"
def accountGppConfig = new AccountGppConfig(code: IAB_US_GENERAL, enabled: true)

and: "Existed account with privacy regulation setup"
def account = getAccountWithAllowActivitiesAndPrivacyModule(accountId, activities, [accountGppConfig])
accountDao.save(account)

and: "Stored request in DB"
def storedRequest = StoredRequest.getStoredRequest(ampRequest, ampStoredRequest)
storedRequestDao.save(storedRequest)

when: "PBS processes amp request"
def response = activityPbsService.sendAmpRequest(ampRequest)

then: "Generic bidder request should have empty EIDS fields"
def genericBidderRequest = bidder.getBidderRequest(ampStoredRequest.id)
verifyAll {
!genericBidderRequest.user.eids
!genericBidderRequest.user?.ext?.eids
}

and: "Response should not contain any warnings"
assert !response.ext.warnings

and: "Response should contain amp error"
assert response.ext?.errors[PREBID]*.code == [999]
assert response.ext?.errors[PREBID]*.message == ["Amp request parameter consent_string has invalid format: $INVALID_GPP_STRING"]
}

def "PBS amp call when request have different gpp consent but match and rejecting should remove EIDS fields in request"() {
given: "Default Generic BidRequest with EIDS fields field and account id"
def accountId = PBSUtils.randomNumber as String
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,10 @@ import org.prebid.server.functional.model.request.amp.AmpRequest
import org.prebid.server.functional.model.request.auction.Activity
import org.prebid.server.functional.model.request.auction.ActivityRule
import org.prebid.server.functional.model.request.auction.AllowActivities
import org.prebid.server.functional.model.request.auction.BidRequest
import org.prebid.server.functional.model.request.auction.Condition
import org.prebid.server.functional.model.request.auction.Data
import org.prebid.server.functional.model.request.auction.Device
import org.prebid.server.functional.model.request.auction.Eid
import org.prebid.server.functional.model.request.auction.Geo
import org.prebid.server.functional.model.request.auction.RegsExt
import org.prebid.server.functional.model.request.auction.User
import org.prebid.server.functional.model.request.auction.UserExt
import org.prebid.server.functional.model.request.auction.UserExtData
import org.prebid.server.functional.service.PrebidServerException
import org.prebid.server.functional.util.PBSUtils
import org.prebid.server.functional.util.privacy.gpp.UsCaV1Consent
Expand Down Expand Up @@ -87,6 +81,7 @@ import static org.prebid.server.functional.model.request.auction.PrivacyModule.I
import static org.prebid.server.functional.model.request.auction.PrivacyModule.IAB_US_CUSTOM_LOGIC
import static org.prebid.server.functional.model.request.auction.PrivacyModule.IAB_US_GENERAL
import static org.prebid.server.functional.model.request.auction.TraceLevel.VERBOSE
import static org.prebid.server.functional.model.response.auction.ErrorType.PREBID
import static org.prebid.server.functional.util.privacy.model.State.ALABAMA
import static org.prebid.server.functional.util.privacy.model.State.ONTARIO

Expand Down Expand Up @@ -1114,6 +1109,59 @@ class GppTransmitUfpdActivitiesSpec extends PrivacyBaseSpec {
]
}

def "PBS auction call when privacy module contain invalid GPP string should remove UFPD fields in request"() {
given: "Default Generic BidRequests with UFPD fields and account id"
def accountId = PBSUtils.randomNumber as String
def bidRequest = getBidRequestWithPersonalData(accountId).tap {
regs.gppSid = [US_NAT_V1.intValue]
regs.gpp = INVALID_GPP_STRING
}

and: "Activities set for transmitUfpd with rejecting privacy regulation"
def rule = new ActivityRule().tap {
it.privacyRegulation = [IAB_US_GENERAL]
}

def activities = AllowActivities.getDefaultAllowActivities(TRANSMIT_UFPD, Activity.getDefaultActivity([rule]))

and: "Account gpp configuration"
def accountGppConfig = new AccountGppConfig(code: IAB_US_GENERAL, enabled: true)

and: "Existed account with privacy regulation setup"
def account = getAccountWithAllowActivitiesAndPrivacyModule(accountId, activities, [accountGppConfig])
accountDao.save(account)

when: "PBS processes auction requests"
def response= activityPbsService.sendAuctionRequest(bidRequest)

then: "Generic bidder request should have empty UFPD fields"
def bidderRequest = bidder.getBidderRequest(bidRequest.id)
verifyAll {
!bidderRequest.device.didsha1
!bidderRequest.device.didmd5
!bidderRequest.device.dpidsha1
!bidderRequest.device.ifa
!bidderRequest.device.macsha1
!bidderRequest.device.macmd5
!bidderRequest.device.dpidmd5
!bidderRequest.user.id
!bidderRequest.user.buyeruid
!bidderRequest.user.yob
!bidderRequest.user.gender
!bidderRequest.user.data
!bidderRequest.user.ext
}

and: "Generic bidder request should have data in EIDS fields"
assert bidderRequest.user.eids == bidRequest.user.eids

and: "Response should not contain any warnings"
assert !response.ext.warnings

and: "Response should not contain any errors"
assert !response.ext.errors
}

def "PBS auction call when request have different gpp consent but match and rejecting should remove UFPD fields in request"() {
given: "Default Generic BidRequests with UFPD fields and account id"
def accountId = PBSUtils.randomNumber as String
Expand Down Expand Up @@ -2387,6 +2435,69 @@ class GppTransmitUfpdActivitiesSpec extends PrivacyBaseSpec {
]
}

def "PBS amp call when privacy module contain invalid GPP string should remove UFPD fields in request"() {
given: "Default Generic BidRequest with UFPD fields field and account id"
def accountId = PBSUtils.randomNumber as String
def ampStoredRequest = getBidRequestWithPersonalData(accountId)

and: "amp request with link to account"
def ampRequest = AmpRequest.defaultAmpRequest.tap {
it.account = accountId
it.gppSid = US_NAT_V1.value
it.consentString = INVALID_GPP_STRING
it.consentType = GPP
}

and: "Activities set for transmitUfpd with allowing privacy regulation"
def rule = new ActivityRule().tap {
it.privacyRegulation = [IAB_US_GENERAL]
}

def activities = AllowActivities.getDefaultAllowActivities(TRANSMIT_UFPD, Activity.getDefaultActivity([rule]))

and: "Account gpp configuration"
def accountGppConfig = new AccountGppConfig(code: IAB_US_GENERAL, enabled: true)

and: "Existed account with privacy regulation setup"
def account = getAccountWithAllowActivitiesAndPrivacyModule(accountId, activities, [accountGppConfig])
accountDao.save(account)

and: "Stored request in DB"
def storedRequest = StoredRequest.getStoredRequest(ampRequest, ampStoredRequest)
storedRequestDao.save(storedRequest)

when: "PBS processes amp request"
def response = activityPbsService.sendAmpRequest(ampRequest)

then: "Generic bidder request should have empty UFPD fields"
def bidderRequest = bidder.getBidderRequest(ampStoredRequest.id)
verifyAll {
!bidderRequest.device.didsha1
!bidderRequest.device.didmd5
!bidderRequest.device.dpidsha1
!bidderRequest.device.ifa
!bidderRequest.device.macsha1
!bidderRequest.device.macmd5
!bidderRequest.device.dpidmd5
!bidderRequest.user.id
!bidderRequest.user.buyeruid
!bidderRequest.user.yob
!bidderRequest.user.gender
!bidderRequest.user.data
!bidderRequest.user.ext
}

and: "Generic bidder request should have data in EIDS fields"
assert bidderRequest.user.eids == ampStoredRequest.user.eids

and: "Response should not contain any warnings"
assert !response.ext.warnings

and: "Response should contain amp error"
assert response.ext?.errors[PREBID]*.code == [999]
assert response.ext?.errors[PREBID]*.message == ["Amp request parameter consent_string has invalid format: $INVALID_GPP_STRING"]
}

def "PBS amp call when request have different gpp consent but match and rejecting should remove UFPD fields in request"() {
given: "Default Generic BidRequest with UFPD fields field and account id"
def accountId = PBSUtils.randomNumber as String
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ abstract class PrivacyBaseSpec extends BaseSpec {
private static final Map<String, String> GDPR_EEA_COUNTRY = ["gdpr.eea-countries": "$BULGARIA.ISOAlpha2, SK, VK" as String]

protected static final String VENDOR_LIST_PATH = "/app/prebid-server/data/vendorlist-v{VendorVersion}/{VendorVersion}.json"
protected static final String INVALID_GPP_STRING = "DBABLA~BVQqAAAAAg.YA" // TODO replace BVQqAAAAAg with ${PBSUtils.getRandomString(7)} when proper fix is ready
protected static final String VALID_VALUE_FOR_GPC_HEADER = "1"
protected static final GppConsent SIMPLE_GPC_DISALLOW_LOGIC = new UsNatV1Consent.Builder().setGpc(true).build()
protected static final VendorList vendorListResponse = new VendorList(networkServiceContainer)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ public void fromShouldReturnGppContextWrapperWithErrorOnInvalidGpp() {
assertThat(gppContext.regions()).isEqualTo(GppContext.Regions.builder().build());
});
assertThat(gppContextWrapper.getErrors())
.containsExactly("GPP string invalid: Unable to decode 'invalid'");
.containsExactly("GPP string invalid: Unable to decode 'invalidA'");
}

@Test
Expand Down
Loading
Loading