Repository: prebid/prebid-server
Pull request #3064 (2023-08-30 19:35:55)

Privacy Activities: Pass Request
Extends the Activities Control infrastructure to pass request information. Implemented Phase 3.5 GPP SID signal as an initial proof of concept for the new request object.


Patch:
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 6ea29bd3..fd790699 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -32,9 +32,25 @@ jobs:
     outputs:
       hasWritePermission: ${{ steps.check.outputs.require-result }}
 
+  build-master:
+    name: Build master
+    needs: check-permission
+    if: contains(needs.check-permission.outputs.hasWritePermission, 'true')
+    runs-on: ubuntu-latest
+    steps:
+      - name: Checkout
+        uses: actions/checkout@master
+        with:
+          fetch-depth: 0
+          repository: ${{ github.repository }}
+          ref: master
+      - name: Build and validate
+        run: |
+          ./validate.sh
+
   publish-tag:
     name: Publish tag
-    needs: check-permission
+    needs: build-master
     if: contains(needs.check-permission.outputs.hasWritePermission, 'true')
     permissions:
       contents: write
diff --git a/config/account.go b/config/account.go
index 038a8daa..e5265072 100644
--- a/config/account.go
+++ b/config/account.go
@@ -85,6 +85,10 @@ func (pf *AccountPriceFloors) validate(errs []error) []error {
 	return errs
 }
 
+func (pf *AccountPriceFloors) IsAdjustForBidAdjustmentEnabled() bool {
+	return pf.AdjustForBidAdjustment
+}
+
 // EnabledForChannelType indicates whether CCPA is turned on at the account level for the specified channel type
 // by using the channel type setting if defined or the general CCPA setting if defined; otherwise it returns nil
 func (a *AccountCCPA) EnabledForChannelType(channelType ChannelType) *bool {
diff --git a/endpoints/cookie_sync.go b/endpoints/cookie_sync.go
index 0321fea3..0a81c2d5 100644
--- a/endpoints/cookie_sync.go
+++ b/endpoints/cookie_sync.go
@@ -19,11 +19,11 @@ import (
 	"github.com/prebid/prebid-server/config"
 	"github.com/prebid/prebid-server/errortypes"
 	"github.com/prebid/prebid-server/gdpr"
+	"github.com/prebid/prebid-server/macros"
 	"github.com/prebid/prebid-server/metrics"
 	"github.com/prebid/prebid-server/openrtb_ext"
 	"github.com/prebid/prebid-server/privacy"
 	"github.com/prebid/prebid-server/privacy/ccpa"
-	gdprPrivacy "github.com/prebid/prebid-server/privacy/gdpr"
 	gppPrivacy "github.com/prebid/prebid-server/privacy/gpp"
 	"github.com/prebid/prebid-server/stored_requests"
 	"github.com/prebid/prebid-server/usersync"
@@ -84,7 +84,7 @@ type cookieSyncEndpoint struct {
 }
 
 func (c *cookieSyncEndpoint) Handle(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
-	request, privacyPolicies, err := c.parseRequest(r)
+	request, privacyMacros, err := c.parseRequest(r)
 	if err != nil {
 		c.writeParseRequestErrorMetrics(err)
 		c.handleError(w, err, http.StatusBadRequest)
@@ -102,24 +102,24 @@ func (c *cookieSyncEndpoint) Handle(w http.ResponseWriter, r *http.Request, _ ht
 		c.handleError(w, errCookieSyncOptOut, http.StatusUnauthorized)
 	case usersync.StatusBlockedByGDPR:
 		c.metrics.RecordCookieSync(metrics.CookieSyncGDPRHostCookieBlocked)
-		c.handleResponse(w, request.SyncTypeFilter, cookie, privacyPolicies, nil)
+		c.handleResponse(w, request.SyncTypeFilter, cookie, privacyMacros, nil)
 	case usersync.StatusOK:
 		c.metrics.RecordCookieSync(metrics.CookieSyncOK)
 		c.writeSyncerMetrics(result.BiddersEvaluated)
-		c.handleResponse(w, request.SyncTypeFilter, cookie, privacyPolicies, result.SyncersChosen)
+		c.handleResponse(w, request.SyncTypeFilter, cookie, privacyMacros, result.SyncersChosen)
 	}
 }
 
-func (c *cookieSyncEndpoint) parseRequest(r *http.Request) (usersync.Request, privacy.Policies, error) {
+func (c *cookieSyncEndpoint) parseRequest(r *http.Request) (usersync.Request, macros.UserSyncPrivacy, error) {
 	defer r.Body.Close()
 	body, err := io.ReadAll(r.Body)
 	if err != nil {
-		return usersync.Request{}, privacy.Policies{}, errCookieSyncBody
+		return usersync.Request{}, macros.UserSyncPrivacy{}, errCookieSyncBody
 	}
 
 	request := cookieSyncRequest{}
 	if err := json.Unmarshal(body, &request); err != nil {
-		return usersync.Request{}, privacy.Policies{}, fmt.Errorf("JSON parsing failed: %s", err.Error())
+		return usersync.Request{}, macros.UserSyncPrivacy{}, fmt.Errorf("JSON parsing failed: %s", err.Error())
 	}
 
 	if request.Account == "" {
@@ -127,22 +127,22 @@ func (c *cookieSyncEndpoint) parseRequest(r *http.Request) (usersync.Request, pr
 	}
 	account, fetchErrs := accountService.GetAccount(context.Background(), c.config, c.accountsFetcher, request.Account, c.metrics)
 	if len(fetchErrs) > 0 {
-		return usersync.Request{}, privacy.Policies{}, combineErrors(fetchErrs)
+		return usersync.Request{}, macros.UserSyncPrivacy{}, combineErrors(fetchErrs)
 	}
 
 	request = c.setLimit(request, account.CookieSync)
 	request = c.setCooperativeSync(request, account.CookieSync)
 
-	privacyPolicies, gdprSignal, err := extractPrivacyPolicies(request, c.privacyConfig.gdprConfig.DefaultValue)
+	privacyMacros, gdprSignal, privacyPolicies, err := extractPrivacyPolicies(request, c.privacyConfig.gdprConfig.DefaultValue)
 	if err != nil {
-		return usersync.Request{}, privacy.Policies{}, err
+		return usersync.Request{}, macros.UserSyncPrivacy{}, err
 	}
 
 	ccpaParsedPolicy := ccpa.ParsedPolicy{}
 	if request.USPrivacy != "" {
-		parsedPolicy, err := privacyPolicies.CCPA.Parse(c.privacyConfig.bidderHashSet)
+		parsedPolicy, err := ccpa.Policy{Consent: request.USPrivacy}.Parse(c.privacyConfig.bidderHashSet)
 		if err != nil {
-			privacyPolicies.CCPA.Consent = ""
+			privacyMacros.USPrivacy = ""
 		}
 		if c.privacyConfig.ccpaEnforce {
 			ccpaParsedPolicy = parsedPolicy
@@ -153,11 +153,11 @@ func (c *cookieSyncEndpoint) parseRequest(r *http.Request) (usersync.Request, pr
 
 	syncTypeFilter, err := parseTypeFilter(request.FilterSettings)
 	if err != nil {
-		return usersync.Request{}, privacy.Policies{}, err
+		return usersync.Request{}, macros.UserSyncPrivacy{}, err
 	}
 
 	gdprRequestInfo := gdpr.RequestInfo{
-		Consent:    privacyPolicies.GDPR.Consent,
+		Consent:    privacyMacros.GDPRConsent,
 		GDPRSignal: gdprSignal,
 	}
 
@@ -175,22 +175,23 @@ func (c *cookieSyncEndpoint) parseRequest(r *http.Request) (usersync.Request, pr
 			gdprPermissions:  gdprPerms,
 			ccpaParsedPolicy: ccpaParsedPolicy,
 			activityControl:  activityControl,
+			activityRequest:  privacy.NewRequestFromPolicies(privacyPolicies),
 		},
 		SyncTypeFilter: syncTypeFilter,
 	}
-	return rx, privacyPolicies, nil
+	return rx, privacyMacros, nil
 }
 
-func extractPrivacyPolicies(request cookieSyncRequest, usersyncDefaultGDPRValue string) (privacy.Policies, gdpr.Signal, error) {
+func extractPrivacyPolicies(request cookieSyncRequest, usersyncDefaultGDPRValue string) (macros.UserSyncPrivacy, gdpr.Signal, privacy.Policies, error) {
 	// GDPR
-	gppSID, err := stringutil.StrToInt8Slice(request.GPPSid)
+	gppSID, err := stringutil.StrToInt8Slice(request.GPPSID)
 	if err != nil {
-		return privacy.Policies{}, gdpr.SignalNo, err
+		return macros.UserSyncPrivacy{}, gdpr.SignalNo, privacy.Policies{}, err
 	}
 
 	gdprSignal, gdprString, err := extractGDPRSignal(request.GDPR, gppSID)
 	if err != nil {
-		return privacy.Policies{}, gdpr.SignalNo, err
+		return macros.UserSyncPrivacy{}, gdpr.SignalNo, privacy.Policies{}, err
 	}
 
 	var gpp gpplib.GppContainer
@@ -198,7 +199,7 @@ func extractPrivacyPolicies(request cookieSyncRequest, usersyncDefaultGDPRValue
 		var err error
 		gpp, err = gpplib.Parse(request.GPP)
 		if err != nil {
-			return privacy.Policies{}, gdpr.SignalNo, err
+			return macros.UserSyncPrivacy{}, gdpr.SignalNo, privacy.Policies{}, err
 		}
 	}
 
@@ -209,33 +210,33 @@ func extractPrivacyPolicies(request cookieSyncRequest, usersyncDefaultGDPRValue
 
 	if gdprConsent == "" {
 		if gdprSignal == gdpr.SignalYes {
-			return privacy.Policies{}, gdpr.SignalNo, errCookieSyncGDPRConsentMissing
+			return macros.UserSyncPrivacy{}, gdpr.SignalNo, privacy.Policies{}, errCookieSyncGDPRConsentMissing
 		}
 
 		if gdprSignal == gdpr.SignalAmbiguous && gdpr.SignalNormalize(gdprSignal, usersyncDefaultGDPRValue) == gdpr.SignalYes {
-			return privacy.Policies{}, gdpr.SignalNo, errCookieSyncGDPRConsentMissingSignalAmbiguous
+			return macros.UserSyncPrivacy{}, gdpr.SignalNo, privacy.Policies{}, errCookieSyncGDPRConsentMissingSignalAmbiguous
 		}
 	}
 
 	// CCPA
 	ccpaString, err := ccpa.SelectCCPAConsent(request.USPrivacy, gpp, gppSID)
 	if err != nil {
-		return privacy.Policies{}, gdpr.SignalNo, err
+		return macros.UserSyncPrivacy{}, gdpr.SignalNo, privacy.Policies{}, err
 	}
 
-	return privacy.Policies{
-		GDPR: gdprPrivacy.Policy{
-			Signal:  gdprString,
-			Consent: gdprConsent,
-		},
-		CCPA: ccpa.Policy{
-			Consent: ccpaString,
-		},
-		GPP: gppPrivacy.Policy{
-			Consent: request.GPP,
-			RawSID:  request.GPPSid,
-		},
-	}, gdprSignal, nil
+	privacyMacros := macros.UserSyncPrivacy{
+		GDPR:        gdprString,
+		GDPRConsent: gdprConsent,
+		USPrivacy:   ccpaString,
+		GPP:         request.GPP,
+		GPPSID:      request.GPPSID,
+	}
+
+	privacyPolicies := privacy.Policies{
+		GPPSID: gppSID,
+	}
+
+	return privacyMacros, gdprSignal, privacyPolicies, nil
 }
 
 func extractGDPRSignal(requestGDPR *int, gppSID []int8) (gdpr.Signal, string, error) {
@@ -396,7 +397,7 @@ func (c *cookieSyncEndpoint) writeSyncerMetrics(biddersEvaluated []usersync.Bidd
 	}
 }
 
-func (c *cookieSyncEndpoint) handleResponse(w http.ResponseWriter, tf usersync.SyncTypeFilter, co *usersync.Cookie, p privacy.Policies, s []usersync.SyncerChoice) {
+func (c *cookieSyncEndpoint) handleResponse(w http.ResponseWriter, tf usersync.SyncTypeFilter, co *usersync.Cookie, m macros.UserSyncPrivacy, s []usersync.SyncerChoice) {
 	status := "no_cookie"
 	if co.HasAnyLiveSyncs() {
 		status = "ok"
@@ -409,7 +410,7 @@ func (c *cookieSyncEndpoint) handleResponse(w http.ResponseWriter, tf usersync.S
 
 	for _, syncerChoice := range s {
 		syncTypes := tf.ForBidder(syncerChoice.Bidder)
-		sync, err := syncerChoice.Syncer.GetSync(syncTypes, p)
+		sync, err := syncerChoice.Syncer.GetSync(syncTypes, m)
 		if err != nil {
 			glog.Errorf("Failed to get usersync info for %s: %v", syncerChoice.Bidder, err)
 			continue
@@ -460,7 +461,7 @@ type cookieSyncRequest struct {
 	USPrivacy       string                           `json:"us_privacy"`
 	Limit           int                              `json:"limit"`
 	GPP             string                           `json:"gpp"`
-	GPPSid          string                           `json:"gpp_sid"`
+	GPPSID          string                           `json:"gpp_sid"`
 	CooperativeSync *bool                            `json:"coopSync"`
 	FilterSettings  *cookieSyncRequestFilterSettings `json:"filterSettings"`
 	Account         string                           `json:"account"`
@@ -505,6 +506,7 @@ type usersyncPrivacy struct {
 	gdprPermissions  gdpr.Permissions
 	ccpaParsedPolicy ccpa.ParsedPolicy
 	activityControl  privacy.ActivityControl
+	activityRequest  privacy.ActivityRequest
 }
 
 func (p usersyncPrivacy) GDPRAllowsHostCookie() bool {
@@ -523,6 +525,8 @@ func (p usersyncPrivacy) CCPAAllowsBidderSync(bidder string) bool {
 }
 
 func (p usersyncPrivacy) ActivityAllowsUserSync(bidder string) bool {
-	return p.activityControl.Allow(privacy.ActivitySyncUser,
-		privacy.Component{Type: privacy.ComponentTypeBidder, Name: bidder})
+	return p.activityControl.Allow(
+		privacy.ActivitySyncUser,
+		privacy.Component{Type: privacy.ComponentTypeBidder, Name: bidder},
+		p.activityRequest)
 }
diff --git a/endpoints/setuid.go b/endpoints/setuid.go
index b9023cfc..64178444 100644
--- a/endpoints/setuid.go
+++ b/endpoints/setuid.go
@@ -106,8 +106,25 @@ func NewSetUIDEndpoint(cfg *config.Configuration, syncersByBidder map[string]use
 
 		activityControl := privacy.NewActivityControl(&account.Privacy)
 
+		gppSID, err := stringutil.StrToInt8Slice(query.Get("gpp_sid"))
+		if err != nil {
+			err := fmt.Errorf("invalid gpp_sid encoding, must be a csv list of integers")
+			w.WriteHeader(http.StatusBadRequest)
+			w.Write([]byte(err.Error()))
+			metricsEngine.RecordSetUid(metrics.SetUidBadRequest)
+			so.Errors = []error{err}
+			so.Status = http.StatusBadRequest
+			return
+		}
+
+		policies := privacy.Policies{
+			GPPSID: gppSID,
+		}
+
 		userSyncActivityAllowed := activityControl.Allow(privacy.ActivitySyncUser,
-			privacy.Component{Type: privacy.ComponentTypeBidder, Name: bidderName})
+			privacy.Component{Type: privacy.ComponentTypeBidder, Name: bidderName},
+			privacy.NewRequestFromPolicies(policies))
+
 		if !userSyncActivityAllowed {
 			w.WriteHeader(http.StatusUnavailableForLegalReasons)
 			return
diff --git a/exchange/adapter_builders.go b/exchange/adapter_builders.go
index 484fe59c..c5e9dc49 100755
--- a/exchange/adapter_builders.go
+++ b/exchange/adapter_builders.go
@@ -287,6 +287,7 @@ func newAdapterBuilders() map[openrtb_ext.BidderName]adapters.Builder {
 		openrtb_ext.BidderGamoshi:           gamoshi.Builder,
 		openrtb_ext.BidderGlobalsun:         globalsun.Builder,
 		openrtb_ext.BidderGothamads:         gothamads.Builder,
+		openrtb_ext.BidderGreedygame:        limelightDigital.Builder,
 		openrtb_ext.BidderGrid:              grid.Builder,
 		openrtb_ext.BidderGumGum:            gumgum.Builder,
 		openrtb_ext.BidderHuaweiAds:         huaweiads.Builder,
diff --git a/exchange/exchange.go b/exchange/exchange.go
index 70d92c08..136f1040 100644
--- a/exchange/exchange.go
+++ b/exchange/exchange.go
@@ -319,7 +319,7 @@ func (e *exchange) HoldAuction(ctx context.Context, r *AuctionRequest, debugLog
 		Prebid: *requestExtPrebid,
 		SChain: requestExt.GetSChain(),
 	}
-	bidderRequests, privacyLabels, errs := e.requestSplitter.cleanOpenRTBRequests(ctx, *r, requestExtLegacy, gdprDefaultValue)
+	bidderRequests, privacyLabels, errs := e.requestSplitter.cleanOpenRTBRequests(ctx, *r, requestExtLegacy, gdprDefaultValue, bidAdjustmentFactors)
 	errs = append(errs, floorErrs...)
 
 	mergedBidAdj, err := bidadjustment.Merge(r.BidRequestWrapper, r.Account.BidAdjustments)
diff --git a/exchange/utils.go b/exchange/utils.go
index 267d928c..0becca21 100644
--- a/exchange/utils.go
+++ b/exchange/utils.go
@@ -54,9 +54,8 @@ type requestSplitter struct {
 func (rs *requestSplitter) cleanOpenRTBRequests(ctx context.Context,
 	auctionReq AuctionRequest,
 	requestExt *openrtb_ext.ExtRequest,
-	gdprDefaultValue gdpr.Signal,
+	gdprDefaultValue gdpr.Signal, bidAdjustmentFactors map[string]float64,
 ) (allowedBidderRequests []BidderRequest, privacyLabels metrics.PrivacyLabels, errs []error) {
-
 	req := auctionReq.BidRequestWrapper
 	aliases, errs := parseAliases(req.BidRequest)
 	if len(errs) > 0 {
@@ -93,6 +92,11 @@ func (rs *requestSplitter) cleanOpenRTBRequests(ctx context.Context,
 		}
 	}
 
+	if auctionReq.Account.PriceFloors.IsAdjustForBidAdjustmentEnabled() {
+		//Apply BidAdjustmentFactor to imp.BidFloor
+		applyBidAdjustmentToFloor(allBidderRequests, bidAdjustmentFactors)
+	}
+
 	gdprSignal, err := getGDPR(req)
 	if err != nil {
 		errs = append(errs, err)
@@ -153,7 +157,7 @@ func (rs *requestSplitter) cleanOpenRTBRequests(ctx context.Context,
 
 		// fetchBids activity
 		scopedName := privacy.Component{Type: privacy.ComponentTypeBidder, Name: bidderRequest.BidderName.String()}
-		fetchBidsActivityAllowed := auctionReq.Activities.Allow(privacy.ActivityFetchBids, scopedName)
+		fetchBidsActivityAllowed := auctionReq.Activities.Allow(privacy.ActivityFetchBids, scopedName, privacy.NewRequestFromBidRequest(*req))
 		if !fetchBidsActivityAllowed {
 			// skip the call to a bidder if fetchBids activity is not allowed
 			// do not add this bidder to allowedBidderRequests
@@ -173,7 +177,7 @@ func (rs *requestSplitter) cleanOpenRTBRequests(ctx context.Context,
 			}
 		}
 
-		passIDActivityAllowed := auctionReq.Activities.Allow(privacy.ActivityTransmitUserFPD, scopedName)
+		passIDActivityAllowed := auctionReq.Activities.Allow(privacy.ActivityTransmitUserFPD, scopedName, privacy.NewRequestFromBidRequest(*req))
 		if !passIDActivityAllowed {
 			privacyEnforcement.UFPD = true
 		} else {
@@ -190,7 +194,7 @@ func (rs *requestSplitter) cleanOpenRTBRequests(ctx context.Context,
 			privacyEnforcement.CCPA = ccpaEnforcer.ShouldEnforce(bidderRequest.BidderName.String())
 		}
 
-		passGeoActivityAllowed := auctionReq.Activities.Allow(privacy.ActivityTransmitPreciseGeo, scopedName)
+		passGeoActivityAllowed := auctionReq.Activities.Allow(privacy.ActivityTransmitPreciseGeo, scopedName, privacy.NewRequestFromBidRequest(*req))
 		if !passGeoActivityAllowed {
 			privacyEnforcement.PreciseGeo = true
 		} else {
@@ -212,7 +216,7 @@ func (rs *requestSplitter) cleanOpenRTBRequests(ctx context.Context,
 			applyFPD(auctionReq.FirstPartyData[bidderRequest.BidderName], bidderRequest.BidRequest)
 		}
 
-		privacyEnforcement.TID = !auctionReq.Activities.Allow(privacy.ActivityTransmitTids, scopedName)
+		privacyEnforcement.TID = !auctionReq.Activities.Allow(privacy.ActivityTransmitTIDs, scopedName, privacy.NewRequestFromBidRequest(*req))
 
 		privacyEnforcement.Apply(bidderRequest.BidRequest, auctionReq.Account.Privacy)
 		allowedBidderRequests = append(allowedBidderRequests, bidderRequest)
@@ -1071,3 +1075,25 @@ func getPrebidMediaTypeForBid(bid openrtb2.Bid) (openrtb_ext.BidType, error) {
 		Message: errMsg,
 	}
 }
+
+func applyBidAdjustmentToFloor(allBidderRequests []BidderRequest, bidAdjustmentFactors map[string]float64) {
+
+	if len(bidAdjustmentFactors) == 0 {
+		return
+	}
+
+	for _, bidderRequest := range allBidderRequests {
+		bidAdjustment := 1.0
+
+		if bidAdjustemntValue, ok := bidAdjustmentFactors[string(bidderRequest.BidderName)]; ok {
+			bidAdjustment = bidAdjustemntValue
+		}
+
+		if bidAdjustment != 1.0 {
+			for index, imp := range bidderRequest.BidRequest.Imp {
+				imp.BidFloor = imp.BidFloor / bidAdjustment
+				bidderRequest.BidRequest.Imp[index] = imp
+			}
+		}
+	}
+}
diff --git a/macros/macros.go b/macros/macros.go
index 7292e0c8..609e72cd 100644
--- a/macros/macros.go
+++ b/macros/macros.go
@@ -5,7 +5,7 @@ import (
 	"text/template"
 )
 
-// EndpointTemplateParams specifies params for an endpoint template
+// EndpointTemplateParams specifies macros for bidder endpoints.
 type EndpointTemplateParams struct {
 	Host        string
 	PublisherID string
@@ -17,8 +17,8 @@ type EndpointTemplateParams struct {
 	GvlID       string
 }
 
-// UserSyncTemplateParams specifies params for an user sync URL template
-type UserSyncTemplateParams struct {
+// UserSyncPrivacy specifies privacy policy macros, represented as strings, for user sync urls.
+type UserSyncPrivacy struct {
 	GDPR        string
 	GDPRConsent string
 	USPrivacy   string
@@ -30,10 +30,10 @@ type UserSyncTemplateParams struct {
 func ResolveMacros(aTemplate *template.Template, params interface{}) (string, error) {
 	strBuf := bytes.Buffer{}
 
-	err := aTemplate.Execute(&strBuf, params)
-	if err != nil {
+	if err := aTemplate.Execute(&strBuf, params); err != nil {
 		return "", err
 	}
+
 	res := strBuf.String()
 	return res, nil
 }
diff --git a/openrtb_ext/bidders.go b/openrtb_ext/bidders.go
index deeb1c58..11f01988 100644
--- a/openrtb_ext/bidders.go
+++ b/openrtb_ext/bidders.go
@@ -111,6 +111,7 @@ var coreBidderNames []BidderName = []BidderName{
 	BidderGamoshi,
 	BidderGlobalsun,
 	BidderGothamads,
+	BidderGreedygame,
 	BidderGrid,
 	BidderGumGum,
 	BidderHuaweiAds,
@@ -403,6 +404,7 @@ const (
 	BidderGamoshi           BidderName = "gamoshi"
 	BidderGlobalsun         BidderName = "globalsun"
 	BidderGothamads         BidderName = "gothamads"
+	BidderGreedygame        BidderName = "greedygame"
 	BidderGrid              BidderName = "grid"
 	BidderGumGum            BidderName = "gumgum"
 	BidderHuaweiAds         BidderName = "huaweiads"
diff --git a/privacy/activity.go b/privacy/activity.go
index 6ca48ae0..2309d020 100644
--- a/privacy/activity.go
+++ b/privacy/activity.go
@@ -1,6 +1,7 @@
 package privacy
 
-// Activity defines privileges which can be controlled directly by the publisher or via privacy policies.
+// Activity defines Prebid Server actions which can be controlled directly
+// by the publisher or via privacy policies.
 type Activity int
 
 const (
@@ -10,8 +11,8 @@ const (
 	ActivityReportAnalytics
 	ActivityTransmitUserFPD
 	ActivityTransmitPreciseGeo
-	ActivityTransmitUniqueRequestIds
-	ActivityTransmitTids
+	ActivityTransmitUniqueRequestIDs
+	ActivityTransmitTIDs
 )
 
 func (a Activity) String() string {
@@ -28,9 +29,9 @@ func (a Activity) String() string {
 		return "transmitUfpd"
 	case ActivityTransmitPreciseGeo:
 		return "transmitPreciseGeo"
-	case ActivityTransmitUniqueRequestIds:
+	case ActivityTransmitUniqueRequestIDs:
 		return "transmitUniqueRequestIds"
-	case ActivityTransmitTids:
+	case ActivityTransmitTIDs:
 		return "transmitTid"
 	}
 
diff --git a/privacy/activitycontrol.go b/privacy/activitycontrol.go
index 33c138f7..9d6668b3 100644
--- a/privacy/activitycontrol.go
+++ b/privacy/activitycontrol.go
@@ -2,6 +2,7 @@ package privacy
 
 import (
 	"github.com/prebid/prebid-server/config"
+	"github.com/prebid/prebid-server/openrtb_ext"
 )
 
 type ActivityResult int
@@ -14,6 +15,27 @@ const (
 
 const defaultActivityResult = true
 
+func NewRequestFromPolicies(p Policies) ActivityRequest {
+	return ActivityRequest{policies: &p}
+}
+
+func NewRequestFromBidRequest(r openrtb_ext.RequestWrapper) ActivityRequest {
+	return ActivityRequest{bidRequest: &r}
+}
+
+type ActivityRequest struct {
+	policies   *Policies
+	bidRequest *openrtb_ext.RequestWrapper
+}
+
+func (r ActivityRequest) IsPolicies() bool {
+	return r.policies != nil
+}
+
+func (r ActivityRequest) IsBidRequest() bool {
+	return r.bidRequest != nil
+}
+
 type ActivityControl struct {
 	plans map[Activity]ActivityPlan
 }
@@ -32,8 +54,8 @@ func NewActivityControl(cfg *config.AccountPrivacy) ActivityControl {
 	plans[ActivityReportAnalytics] = buildPlan(cfg.AllowActivities.ReportAnalytics)
 	plans[ActivityTransmitUserFPD] = buildPlan(cfg.AllowActivities.TransmitUserFPD)
 	plans[ActivityTransmitPreciseGeo] = buildPlan(cfg.AllowActivities.TransmitPreciseGeo)
-	plans[ActivityTransmitUniqueRequestIds] = buildPlan(cfg.AllowActivities.TransmitUniqueRequestIds)
-	plans[ActivityTransmitTids] = buildPlan(cfg.AllowActivities.TransmitTids)
+	plans[ActivityTransmitUniqueRequestIDs] = buildPlan(cfg.AllowActivities.TransmitUniqueRequestIds)
+	plans[ActivityTransmitTIDs] = buildPlan(cfg.AllowActivities.TransmitTids)
 	ac.plans = plans
 
 	return ac
@@ -50,11 +72,9 @@ func cfgToRules(rules []config.ActivityRule) []Rule {
 	var enfRules []Rule
 
 	for _, r := range rules {
-		var result ActivityResult
+		result := ActivityDeny
 		if r.Allow {
 			result = ActivityAllow
-		} else {
-			result = ActivityDeny
 		}
 
 		er := ConditionRule{
@@ -74,14 +94,14 @@ func cfgToDefaultResult(activityDefault *bool) bool {
 	return *activityDefault
 }
 
-func (e ActivityControl) Allow(activity Activity, target Component) bool {
+func (e ActivityControl) Allow(activity Activity, target Component, request ActivityRequest) bool {
 	plan, planDefined := e.plans[activity]
 
 	if !planDefined {
 		return defaultActivityResult
 	}
 
-	return plan.Evaluate(target)
+	return plan.Evaluate(target, request)
 }
 
 type ActivityPlan struct {
@@ -89,9 +109,9 @@ type ActivityPlan struct {
 	rules         []Rule
 }
 
-func (p ActivityPlan) Evaluate(target Component) bool {
+func (p ActivityPlan) Evaluate(target Component, request ActivityRequest) bool {
 	for _, rule := range p.rules {
-		result := rule.Evaluate(target)
+		result := rule.Evaluate(target, request)
 		if result == ActivityDeny || result == ActivityAllow {
 			return result == ActivityAllow
 		}
diff --git a/privacy/gdpr/policy.go b/privacy/gdpr/validate.go
similarity index 68%
rename from privacy/gdpr/policy.go
rename to privacy/gdpr/validate.go
index 0464a9ff..662ad852 100644
--- a/privacy/gdpr/policy.go
+++ b/privacy/gdpr/validate.go
@@ -4,12 +4,6 @@ import (
 	"github.com/prebid/go-gdpr/vendorconsent"
 )
 
-// Policy represents the GDPR regulation for an OpenRTB bid request.
-type Policy struct {
-	Signal  string
-	Consent string
-}
-
 // ValidateConsent returns true if the consent string is empty or valid per the IAB TCF spec.
 func ValidateConsent(consent string) bool {
 	_, err := vendorconsent.ParseString(consent)
diff --git a/privacy/gdpr/policy_test.go b/privacy/gdpr/validate_test.go
similarity index 100%
rename from privacy/gdpr/policy_test.go
rename to privacy/gdpr/validate_test.go
diff --git a/privacy/gpp/gpp.go b/privacy/gpp/sid.go
similarity index 76%
rename from privacy/gpp/gpp.go
rename to privacy/gpp/sid.go
index 35000406..6e10eac9 100644
--- a/privacy/gpp/gpp.go
+++ b/privacy/gpp/sid.go
@@ -5,13 +5,6 @@ import (
 	gppConstants "github.com/prebid/go-gpp/constants"
 )
 
-// Policy represents the GPP privacy string container.
-// Currently just a placeholder until more expansive support is made.
-type Policy struct {
-	Consent string
-	RawSID  string // This is the CSV format ("2,6") that the IAB recommends for passing the SID(s) on a query string.
-}
-
 // IsSIDInList returns true if the 'sid' value is found in the gppSIDs array. Its logic is used in more than
 // one place in our codebase, therefore it was decided to make it its own function.
 func IsSIDInList(gppSIDs []int8, sid gppConstants.SectionID) bool {
diff --git a/privacy/gpp/gpp_test.go b/privacy/gpp/sid_test.go
similarity index 100%
rename from privacy/gpp/gpp_test.go
rename to privacy/gpp/sid_test.go
diff --git a/privacy/policies.go b/privacy/policies.go
index 214696e1..16fee92c 100644
--- a/privacy/policies.go
+++ b/privacy/policies.go
@@ -1,16 +1,6 @@
 package privacy
 
-import (
-	"github.com/prebid/prebid-server/privacy/ccpa"
-	"github.com/prebid/prebid-server/privacy/gdpr"
-	"github.com/prebid/prebid-server/privacy/gpp"
-	"github.com/prebid/prebid-server/privacy/lmt"
-)
-
-// Policies represents the privacy regulations for an OpenRTB bid request.
+// Policies contains privacy signals and consent for non-OpenRTB activities.
 type Policies struct {
-	CCPA ccpa.Policy
-	GDPR gdpr.Policy
-	LMT  lmt.Policy
-	GPP  gpp.Policy
+	GPPSID []int8
 }
diff --git a/privacy/rule.go b/privacy/rule.go
index 47d0913c..ef370f09 100644
--- a/privacy/rule.go
+++ b/privacy/rule.go
@@ -1,5 +1,5 @@
 package privacy
 
 type Rule interface {
-	Evaluate(target Component) ActivityResult
+	Evaluate(target Component, request ActivityRequest) ActivityResult
 }
diff --git a/privacy/rule_condition.go b/privacy/rule_condition.go
index 705543e5..2fc34506 100644
--- a/privacy/rule_condition.go
+++ b/privacy/rule_condition.go
@@ -1,12 +1,16 @@
 package privacy
 
+// noClausesDefinedResult represents the default return when there is no matching criteria specified.
+const noClausesDefinedResult = true
+
 type ConditionRule struct {
 	result        ActivityResult
 	componentName []string
 	componentType []string
+	gppSID        []int8
 }
 
-func (r ConditionRule) Evaluate(target Component) ActivityResult {
+func (r ConditionRule) Evaluate(target Component, request ActivityRequest) ActivityResult {
 	if matched := evaluateComponentName(target, r.componentName); !matched {
 		return ActivityAbstain
 	}
@@ -15,16 +19,19 @@ func (r ConditionRule) Evaluate(target Component) ActivityResult {
 		return ActivityAbstain
 	}
 
+	if matched := evaluateGPPSID(r.gppSID, request); !matched {
+		return ActivityAbstain
+	}
+
 	return r.result
 }
 
 func evaluateComponentName(target Component, componentNames []string) bool {
 	// no clauses are considered a match
 	if len(componentNames) == 0 {
-		return true
+		return noClausesDefinedResult
 	}
 
-	// if there are clauses, at least one needs to match
 	for _, n := range componentNames {
 		if target.MatchesName(n) {
 			return true
@@ -35,9 +42,8 @@ func evaluateComponentName(target Component, componentNames []string) bool {
 }
 
 func evaluateComponentType(target Component, componentTypes []string) bool {
-	// no clauses are considered a match
 	if len(componentTypes) == 0 {
-		return true
+		return noClausesDefinedResult
 	}
 
 	// if there are clauses, at least one needs to match
@@ -49,3 +55,30 @@ func evaluateComponentType(target Component, componentTypes []string) bool {
 
 	return false
 }
+
+func evaluateGPPSID(sid []int8, request ActivityRequest) bool {
+	if len(sid) == 0 {
+		return noClausesDefinedResult
+	}
+
+	for _, x := range getGPPSID(request) {
+		for _, y := range sid {
+			if x == y {
+				return true
+			}
+		}
+	}
+	return false
+}
+
+func getGPPSID(request ActivityRequest) []int8 {
+	if request.IsPolicies() {
+		return request.policies.GPPSID
+	}
+
+	if request.IsBidRequest() && request.bidRequest.Regs != nil {
+		return request.bidRequest.Regs.GPPSID
+	}
+
+	return nil
+}
diff --git a/privacy/scrubber.go b/privacy/scrubber.go
index 4cc90c82..59b74a25 100644
--- a/privacy/scrubber.go
+++ b/privacy/scrubber.go
@@ -2,10 +2,11 @@ package privacy
 
 import (
 	"encoding/json"
+	"net"
+
 	"github.com/prebid/prebid-server/config"
 	"github.com/prebid/prebid-server/util/iputil"
 	"github.com/prebid/prebid-server/util/ptrutil"
-	"net"
 
 	"github.com/prebid/openrtb/v19/openrtb2"
 )
@@ -92,9 +93,11 @@ func (s scrubber) ScrubRequest(bidRequest *openrtb2.BidRequest, enforcement Enfo
 	var userExtParsed map[string]json.RawMessage
 	userExtModified := false
 
+	// expressed in two lines because IntelliJ cannot infer the generic type
 	var userCopy *openrtb2.User
 	userCopy = ptrutil.Clone(bidRequest.User)
 
+	// expressed in two lines because IntelliJ cannot infer the generic type
 	var deviceCopy *openrtb2.Device
 	deviceCopy = ptrutil.Clone(bidRequest.Device)
 
@@ -152,8 +155,7 @@ func (s scrubber) ScrubRequest(bidRequest *openrtb2.BidRequest, enforcement Enfo
 	if enforcement.TID {
 		//remove source.tid and imp.ext.tid
 		if bidRequest.Source != nil {
-			var sourceCopy *openrtb2.Source
-			sourceCopy = ptrutil.Clone(bidRequest.Source)
+			sourceCopy := ptrutil.Clone(bidRequest.Source)
 			sourceCopy.TID = ""
 			bidRequest.Source = sourceCopy
 		}
diff --git a/static/bidder-info/greedygame.yaml b/static/bidder-info/greedygame.yaml
new file mode 100644
index 00000000..5e73e1fe
--- /dev/null
+++ b/static/bidder-info/greedygame.yaml
@@ -0,0 +1,16 @@
+endpoint: "http://ads-pbs.rtb-greedygame.com/openrtb/{{.PublisherID}}?host={{.Host}}"
+maintainer:
+  email: "engineering@project-limelight.com"
+capabilities:
+  app:
+    mediaTypes:
+      - banner
+      - video
+      - audio
+      - native
+  site:
+    mediaTypes:
+      - banner
+      - video
+      - audio
+      - native
diff --git a/static/bidder-params/greedygame.json b/static/bidder-params/greedygame.json
new file mode 100644
index 00000000..2057c147
--- /dev/null
+++ b/static/bidder-params/greedygame.json
@@ -0,0 +1,23 @@
+{
+  "$schema": "http://json-schema.org/draft-04/schema#",
+  "title": "GreedyGame Adapter Params",
+  "description": "A schema which validates params accepted by the GreedyGame adapter",
+  "type": "object",
+
+  "properties": {
+    "host": {
+      "type": "string",
+      "description": "Ad network's RTB host",
+      "format": "hostname",
+      "pattern": "^.+\\..+$"
+    },
+    "publisherId": {
+      "type": ["integer", "string"],
+      "description": "Publisher ID",
+      "minimum": 1,
+      "pattern": "^[1-9][0-9]*$"
+    }
+  },
+
+  "required": ["host", "publisherId"]
+}
diff --git a/usersync/syncer.go b/usersync/syncer.go
index fb14201f..e561614f 100644
--- a/usersync/syncer.go
+++ b/usersync/syncer.go
@@ -11,14 +11,13 @@ import (
 	validator "github.com/asaskevich/govalidator"
 	"github.com/prebid/prebid-server/config"
 	"github.com/prebid/prebid-server/macros"
-	"github.com/prebid/prebid-server/privacy"
 )
 
 var (
-	errNoSyncTypesProvided        = errors.New("no sync types provided")
-	errNoSyncTypesSupported       = errors.New("no sync types supported")
-	errDefaultTypeMissingIFrame   = errors.New("default is set to iframe but no iframe endpoint is configured")
-	errDefaultTypeMissingRedirect = errors.New("default is set to redirect but no redirect endpoint is configured")
+	ErrSyncerEndpointRequired = errors.New("at least one endpoint (iframe and/or redirect) is required")
+	ErrSyncerKeyRequired      = errors.New("key is required")
+	errNoSyncTypesProvided    = errors.New("no sync types provided")
+	errNoSyncTypesSupported   = errors.New("no sync types supported")
 )
 
 // Syncer represents the user sync configuration for a bidder or a shared set of bidders.
@@ -35,7 +34,7 @@ type Syncer interface {
 
 	// GetSync returns a user sync for the user's device to perform, or an error if the none of the
 	// sync types are supported or if macro substitution fails.
-	GetSync(syncTypes []SyncType, privacyPolicies privacy.Policies) (Sync, error)
+	GetSync(syncTypes []SyncType, userSyncMacros macros.UserSyncPrivacy) (Sync, error)
 }
 
 // Sync represents a user sync to be performed by the user's device.
@@ -58,9 +57,6 @@ const (
 	setuidSyncTypeRedirect = "i" // i = image response
 )
 
-var ErrSyncerEndpointRequired = errors.New("at least one endpoint (iframe and/or redirect) is required")
-var ErrSyncerKeyRequired = errors.New("key is required")
-
 // NewSyncer creates a new Syncer from the provided configuration, or return an error if macro substition
 // fails or an endpoint url is invalid.
 func NewSyncer(hostConfig config.UserSync, syncerConfig config.Syncer, bidder string) (Syncer, error) {
@@ -170,7 +166,7 @@ func escapeTemplate(x string) string {
 	return escaped.String()
 }
 
-var templateTestValues = macros.UserSyncTemplateParams{
+var templateTestValues = macros.UserSyncPrivacy{
 	GDPR:        "anyGDPR",
 	GDPRConsent: "anyGDPRConsent",
 	USPrivacy:   "anyCCPAConsent",
@@ -219,7 +215,7 @@ func (s standardSyncer) filterSupportedSyncTypes(syncTypes []SyncType) []SyncTyp
 	return supported
 }
 
-func (s standardSyncer) GetSync(syncTypes []SyncType, privacyPolicies privacy.Policies) (Sync, error) {
+func (s standardSyncer) GetSync(syncTypes []SyncType, userSyncMacros macros.UserSyncPrivacy) (Sync, error) {
 	syncType, err := s.chooseSyncType(syncTypes)
 	if err != nil {
 		return Sync{}, err
@@ -227,13 +223,7 @@ func (s standardSyncer) GetSync(syncTypes []SyncType, privacyPolicies privacy.Po
 
 	syncTemplate := s.chooseTemplate(syncType)
 
-	url, err := macros.ResolveMacros(syncTemplate, macros.UserSyncTemplateParams{
-		GDPR:        privacyPolicies.GDPR.Signal,
-		GDPRConsent: privacyPolicies.GDPR.Consent,
-		USPrivacy:   privacyPolicies.CCPA.Consent,
-		GPP:         privacyPolicies.GPP.Consent,
-		GPPSID:      privacyPolicies.GPP.RawSID,
-	})
+	url, err := macros.ResolveMacros(syncTemplate, userSyncMacros)
 	if err != nil {
 		return Sync{}, err
 	}


Test patch:
diff --git a/endpoints/cookie_sync_test.go b/endpoints/cookie_sync_test.go
index a674e9db..68ab0ced 100644
--- a/endpoints/cookie_sync_test.go
+++ b/endpoints/cookie_sync_test.go
@@ -16,12 +16,11 @@ import (
 	"github.com/prebid/prebid-server/config"
 	"github.com/prebid/prebid-server/errortypes"
 	"github.com/prebid/prebid-server/gdpr"
+	"github.com/prebid/prebid-server/macros"
 	"github.com/prebid/prebid-server/metrics"
 	"github.com/prebid/prebid-server/openrtb_ext"
 	"github.com/prebid/prebid-server/privacy"
 	"github.com/prebid/prebid-server/privacy/ccpa"
-	gdprPrivacy "github.com/prebid/prebid-server/privacy/gdpr"
-	gppPrivacy "github.com/prebid/prebid-server/privacy/gpp"
 	"github.com/prebid/prebid-server/usersync"
 	"github.com/prebid/prebid-server/util/ptrutil"
 
@@ -102,7 +101,7 @@ func TestCookieSyncHandle(t *testing.T) {
 	syncTypeExpected := []usersync.SyncType{usersync.SyncTypeIFrame, usersync.SyncTypeRedirect}
 	sync := usersync.Sync{URL: "aURL", Type: usersync.SyncTypeRedirect, SupportCORS: true}
 	syncer := MockSyncer{}
-	syncer.On("GetSync", syncTypeExpected, privacy.Policies{}).Return(sync, nil).Maybe()
+	syncer.On("GetSync", syncTypeExpected, macros.UserSyncPrivacy{}).Return(sync, nil).Maybe()
 
 	cookieWithSyncs := usersync.NewCookie()
 	cookieWithSyncs.Sync("foo", "anyID")
@@ -366,7 +365,7 @@ func TestExtractGDPRSignal(t *testing.T) {
 			expected: testOutput{
 				gdprSignal: gdpr.SignalAmbiguous,
 				gdprString: "2",
-				err:        &errortypes.BadInput{"GDPR signal should be integer 0 or 1"},
+				err:        &errortypes.BadInput{Message: "GDPR signal should be integer 0 or 1"},
 			},
 		},
 		{
@@ -398,8 +397,9 @@ func TestExtractPrivacyPolicies(t *testing.T) {
 		usersyncDefaultGDPRValue string
 	}
 	type testOutput struct {
-		policies   privacy.Policies
+		macros     macros.UserSyncPrivacy
 		gdprSignal gdpr.Signal
+		policies   privacy.Policies
 		err        error
 	}
 	testCases := []struct {
@@ -413,8 +413,9 @@ func TestExtractPrivacyPolicies(t *testing.T) {
 				request: cookieSyncRequest{GPP: "malformedGPPString"},
 			},
 			expected: testOutput{
-				policies:   privacy.Policies{},
+				macros:     macros.UserSyncPrivacy{},
 				gdprSignal: gdpr.SignalNo,
+				policies:   privacy.Policies{},
 				err:        errors.New("error parsing GPP header, header must have type=3"),
 			},
 		},
@@ -423,14 +424,15 @@ func TestExtractPrivacyPolicies(t *testing.T) {
 			in: testInput{
 				request: cookieSyncRequest{
 					GPP:       "DBACNYA~CPXxRfAPXxRfAAfKABENB-CgAAAAAAAAAAYgAAAAAAAA~1YNN",
-					GPPSid:    "malformed",
+					GPPSID:    "malformed",
 					USPrivacy: "1YYY",
 				},
 			},
 			expected: testOutput{
-				policies:   privacy.Policies{},
+				macros:     macros.UserSyncPrivacy{},
 				gdprSignal: gdpr.SignalNo,
-				err:        &strconv.NumError{"ParseInt", "malformed", strconv.ErrSyntax},
+				policies:   privacy.Policies{},
+				err:        &strconv.NumError{Func: "ParseInt", Num: "malformed", Err: strconv.ErrSyntax},
 			},
 		},
 		{
@@ -438,13 +440,14 @@ func TestExtractPrivacyPolicies(t *testing.T) {
 			in: testInput{
 				request: cookieSyncRequest{
 					GPP:       "DBACNYA~CPXxRfAPXxRfAAfKABENB-CgAAAAAAAAAAYgAAAAAAAA~1YNN",
-					GPPSid:    "6",
+					GPPSID:    "6",
 					USPrivacy: "1YYY",
 				},
 			},
 			expected: testOutput{
-				policies:   privacy.Policies{},
+				macros:     macros.UserSyncPrivacy{},
 				gdprSignal: gdpr.SignalNo,
+				policies:   privacy.Policies{},
 				err:        errors.New("request.us_privacy consent does not match uspv1"),
 			},
 		},
@@ -453,40 +456,36 @@ func TestExtractPrivacyPolicies(t *testing.T) {
 			in: testInput{
 				request: cookieSyncRequest{
 					GPP:    "DBACNYA~CPXxRfAPXxRfAAfKABENB-CgAAAAAAAAAAYgAAAAAAAA~1YNN",
-					GPPSid: "6",
+					GPPSID: "6",
 				},
 			},
 			expected: testOutput{
-				policies: privacy.Policies{
-					GDPR: gdprPrivacy.Policy{
-						Signal:  "0",
-						Consent: "CPXxRfAPXxRfAAfKABENB-CgAAAAAAAAAAYgAAAAAAAA",
-					},
-					CCPA: ccpa.Policy{
-						Consent: "1YNN",
-					},
-					GPP: gppPrivacy.Policy{
-						Consent: "DBACNYA~CPXxRfAPXxRfAAfKABENB-CgAAAAAAAAAAYgAAAAAAAA~1YNN",
-						RawSID:  "6",
-					},
+				macros: macros.UserSyncPrivacy{
+					GDPR:        "0",
+					GDPRConsent: "CPXxRfAPXxRfAAfKABENB-CgAAAAAAAAAAYgAAAAAAAA",
+					USPrivacy:   "1YNN",
+					GPP:         "DBACNYA~CPXxRfAPXxRfAAfKABENB-CgAAAAAAAAAAYgAAAAAAAA~1YNN",
+					GPPSID:      "6",
 				},
 				gdprSignal: gdpr.SignalNo,
+				policies:   privacy.Policies{GPPSID: []int8{6}},
 				err:        nil,
 			},
 		},
 	}
 	for _, tc := range testCases {
-		// run
-		outPolicies, outSignal, outErr := extractPrivacyPolicies(tc.in.request, tc.in.usersyncDefaultGDPRValue)
-		// assertions
-		assert.Equal(t, tc.expected.policies, outPolicies, tc.desc)
+		outMacros, outSignal, outPolicies, outErr := extractPrivacyPolicies(tc.in.request, tc.in.usersyncDefaultGDPRValue)
+
+		assert.Equal(t, tc.expected.macros, outMacros, tc.desc)
 		assert.Equal(t, tc.expected.gdprSignal, outSignal, tc.desc)
+		assert.Equal(t, tc.expected.policies, outPolicies, tc.desc)
 		assert.Equal(t, tc.expected.err, outErr, tc.desc)
 	}
 }
 
 func TestCookieSyncParseRequest(t *testing.T) {
 	expectedCCPAParsedPolicy, _ := ccpa.Policy{Consent: "1NYN"}.Parse(map[string]struct{}{})
+	emptyActivityPoliciesRequest := privacy.NewRequestFromPolicies(privacy.Policies{})
 
 	testCases := []struct {
 		description          string
@@ -496,7 +495,7 @@ func TestCookieSyncParseRequest(t *testing.T) {
 		givenCCPAEnabled     bool
 		givenAccountRequired bool
 		expectedError        string
-		expectedPrivacy      privacy.Policies
+		expectedPrivacy      macros.UserSyncPrivacy
 		expectedRequest      usersync.Request
 	}{
 
@@ -521,18 +520,12 @@ func TestCookieSyncParseRequest(t *testing.T) {
 					PriorityGroups:   [][]string{{"a", "b", "c"}},
 				},
 			},
-			expectedPrivacy: privacy.Policies{
-				GDPR: gdprPrivacy.Policy{
-					Signal:  "1",
-					Consent: "CPXxRfAPXxRfAAfKABENB-CgAAAAAAAAAAYgAAAAAAAA",
-				},
-				CCPA: ccpa.Policy{
-					Consent: "1NYN",
-				},
-				GPP: gppPrivacy.Policy{
-					Consent: "DBABMA~CPXxRfAPXxRfAAfKABENB-CgAAAAAAAAAAYgAAAAAAAA",
-					RawSID:  "2",
-				},
+			expectedPrivacy: macros.UserSyncPrivacy{
+				GDPR:        "1",
+				GDPRConsent: "CPXxRfAPXxRfAAfKABENB-CgAAAAAAAAAAYgAAAAAAAA",
+				USPrivacy:   "1NYN",
+				GPP:         "DBABMA~CPXxRfAPXxRfAAfKABENB-CgAAAAAAAAAAYgAAAAAAAA",
+				GPPSID:      "2",
 			},
 			expectedRequest: usersync.Request{
 				Bidders: []string{"a", "b"},
@@ -544,6 +537,7 @@ func TestCookieSyncParseRequest(t *testing.T) {
 				Privacy: usersyncPrivacy{
 					gdprPermissions:  &fakePermissions{},
 					ccpaParsedPolicy: expectedCCPAParsedPolicy,
+					activityRequest:  privacy.NewRequestFromPolicies(privacy.Policies{GPPSID: []int8{2}}),
 				},
 				SyncTypeFilter: usersync.SyncTypeFilter{
 					IFrame:   usersync.NewUniformBidderFilter(usersync.BidderFilterModeInclude),
@@ -568,14 +562,10 @@ func TestCookieSyncParseRequest(t *testing.T) {
 					PriorityGroups:   [][]string{{"a", "b", "c"}},
 				},
 			},
-			expectedPrivacy: privacy.Policies{
-				GDPR: gdprPrivacy.Policy{
-					Signal:  "1",
-					Consent: "anyGDPRConsent",
-				},
-				CCPA: ccpa.Policy{
-					Consent: "1NYN",
-				},
+			expectedPrivacy: macros.UserSyncPrivacy{
+				GDPR:        "1",
+				GDPRConsent: "anyGDPRConsent",
+				USPrivacy:   "1NYN",
 			},
 			expectedRequest: usersync.Request{
 				Bidders: []string{"a", "b"},
@@ -587,6 +577,7 @@ func TestCookieSyncParseRequest(t *testing.T) {
 				Privacy: usersyncPrivacy{
 					gdprPermissions:  &fakePermissions{},
 					ccpaParsedPolicy: expectedCCPAParsedPolicy,
+					activityRequest:  emptyActivityPoliciesRequest,
 				},
 				SyncTypeFilter: usersync.SyncTypeFilter{
 					IFrame:   usersync.NewUniformBidderFilter(usersync.BidderFilterModeInclude),
@@ -599,10 +590,11 @@ func TestCookieSyncParseRequest(t *testing.T) {
 			givenBody:        strings.NewReader(`{}`),
 			givenGDPRConfig:  config.GDPR{Enabled: true, DefaultValue: "0"},
 			givenCCPAEnabled: true,
-			expectedPrivacy:  privacy.Policies{},
+			expectedPrivacy:  macros.UserSyncPrivacy{},
 			expectedRequest: usersync.Request{
 				Privacy: usersyncPrivacy{
 					gdprPermissions: &fakePermissions{},
+					activityRequest: emptyActivityPoliciesRequest,
 				},
 				SyncTypeFilter: usersync.SyncTypeFilter{
 					IFrame:   usersync.NewUniformBidderFilter(usersync.BidderFilterModeInclude),
@@ -621,7 +613,7 @@ func TestCookieSyncParseRequest(t *testing.T) {
 					PriorityGroups:   [][]string{{"a", "b", "c"}},
 				},
 			},
-			expectedPrivacy: privacy.Policies{},
+			expectedPrivacy: macros.UserSyncPrivacy{},
 			expectedRequest: usersync.Request{
 				Cooperative: usersync.Cooperative{
 					Enabled:        true,
@@ -629,6 +621,7 @@ func TestCookieSyncParseRequest(t *testing.T) {
 				},
 				Privacy: usersyncPrivacy{
 					gdprPermissions: &fakePermissions{},
+					activityRequest: emptyActivityPoliciesRequest,
 				},
 				SyncTypeFilter: usersync.SyncTypeFilter{
 					IFrame:   usersync.NewUniformBidderFilter(usersync.BidderFilterModeInclude),
@@ -647,7 +640,7 @@ func TestCookieSyncParseRequest(t *testing.T) {
 					PriorityGroups:   [][]string{{"a", "b", "c"}},
 				},
 			},
-			expectedPrivacy: privacy.Policies{},
+			expectedPrivacy: macros.UserSyncPrivacy{},
 			expectedRequest: usersync.Request{
 				Cooperative: usersync.Cooperative{
 					Enabled:        false,
@@ -655,6 +648,7 @@ func TestCookieSyncParseRequest(t *testing.T) {
 				},
 				Privacy: usersyncPrivacy{
 					gdprPermissions: &fakePermissions{},
+					activityRequest: emptyActivityPoliciesRequest,
 				},
 				SyncTypeFilter: usersync.SyncTypeFilter{
 					IFrame:   usersync.NewUniformBidderFilter(usersync.BidderFilterModeInclude),
@@ -673,7 +667,7 @@ func TestCookieSyncParseRequest(t *testing.T) {
 					PriorityGroups:   [][]string{{"a", "b", "c"}},
 				},
 			},
-			expectedPrivacy: privacy.Policies{},
+			expectedPrivacy: macros.UserSyncPrivacy{},
 			expectedRequest: usersync.Request{
 				Cooperative: usersync.Cooperative{
 					Enabled:        false,
@@ -681,6 +675,7 @@ func TestCookieSyncParseRequest(t *testing.T) {
 				},
 				Privacy: usersyncPrivacy{
 					gdprPermissions: &fakePermissions{},
+					activityRequest: emptyActivityPoliciesRequest,
 				},
 				SyncTypeFilter: usersync.SyncTypeFilter{
 					IFrame:   usersync.NewUniformBidderFilter(usersync.BidderFilterModeInclude),
@@ -699,7 +694,7 @@ func TestCookieSyncParseRequest(t *testing.T) {
 					PriorityGroups:   [][]string{{"a", "b", "c"}},
 				},
 			},
-			expectedPrivacy: privacy.Policies{},
+			expectedPrivacy: macros.UserSyncPrivacy{},
 			expectedRequest: usersync.Request{
 				Cooperative: usersync.Cooperative{
 					Enabled:        false,
@@ -707,6 +702,7 @@ func TestCookieSyncParseRequest(t *testing.T) {
 				},
 				Privacy: usersyncPrivacy{
 					gdprPermissions: &fakePermissions{},
+					activityRequest: emptyActivityPoliciesRequest,
 				},
 				SyncTypeFilter: usersync.SyncTypeFilter{
 					IFrame:   usersync.NewUniformBidderFilter(usersync.BidderFilterModeInclude),
@@ -725,7 +721,7 @@ func TestCookieSyncParseRequest(t *testing.T) {
 					PriorityGroups:   [][]string{{"a", "b", "c"}},
 				},
 			},
-			expectedPrivacy: privacy.Policies{},
+			expectedPrivacy: macros.UserSyncPrivacy{},
 			expectedRequest: usersync.Request{
 				Cooperative: usersync.Cooperative{
 					Enabled:        true,
@@ -733,6 +729,7 @@ func TestCookieSyncParseRequest(t *testing.T) {
 				},
 				Privacy: usersyncPrivacy{
 					gdprPermissions: &fakePermissions{},
+					activityRequest: emptyActivityPoliciesRequest,
 				},
 				SyncTypeFilter: usersync.SyncTypeFilter{
 					IFrame:   usersync.NewUniformBidderFilter(usersync.BidderFilterModeInclude),
@@ -751,7 +748,7 @@ func TestCookieSyncParseRequest(t *testing.T) {
 					PriorityGroups:   [][]string{{"a", "b", "c"}},
 				},
 			},
-			expectedPrivacy: privacy.Policies{},
+			expectedPrivacy: macros.UserSyncPrivacy{},
 			expectedRequest: usersync.Request{
 				Cooperative: usersync.Cooperative{
 					Enabled:        true,
@@ -759,6 +756,7 @@ func TestCookieSyncParseRequest(t *testing.T) {
 				},
 				Privacy: usersyncPrivacy{
 					gdprPermissions: &fakePermissions{},
+					activityRequest: emptyActivityPoliciesRequest,
 				},
 				SyncTypeFilter: usersync.SyncTypeFilter{
 					IFrame:   usersync.NewUniformBidderFilter(usersync.BidderFilterModeInclude),
@@ -771,10 +769,11 @@ func TestCookieSyncParseRequest(t *testing.T) {
 			givenBody:        strings.NewReader(`{"us_privacy":"invalid"}`),
 			givenGDPRConfig:  config.GDPR{Enabled: true, DefaultValue: "0"},
 			givenCCPAEnabled: true,
-			expectedPrivacy:  privacy.Policies{},
+			expectedPrivacy:  macros.UserSyncPrivacy{},
 			expectedRequest: usersync.Request{
 				Privacy: usersyncPrivacy{
 					gdprPermissions: &fakePermissions{},
+					activityRequest: emptyActivityPoliciesRequest,
 				},
 				SyncTypeFilter: usersync.SyncTypeFilter{
 					IFrame:   usersync.NewUniformBidderFilter(usersync.BidderFilterModeInclude),
@@ -787,13 +786,13 @@ func TestCookieSyncParseRequest(t *testing.T) {
 			givenBody:        strings.NewReader(`{"us_privacy":"1NYN"}`),
 			givenGDPRConfig:  config.GDPR{Enabled: true, DefaultValue: "0"},
 			givenCCPAEnabled: false,
-			expectedPrivacy: privacy.Policies{
-				CCPA: ccpa.Policy{
-					Consent: "1NYN"},
+			expectedPrivacy: macros.UserSyncPrivacy{
+				USPrivacy: "1NYN",
 			},
 			expectedRequest: usersync.Request{
 				Privacy: usersyncPrivacy{
 					gdprPermissions: &fakePermissions{},
+					activityRequest: emptyActivityPoliciesRequest,
 				},
 				SyncTypeFilter: usersync.SyncTypeFilter{
 					IFrame:   usersync.NewUniformBidderFilter(usersync.BidderFilterModeInclude),
@@ -827,12 +826,13 @@ func TestCookieSyncParseRequest(t *testing.T) {
 			givenBody:        strings.NewReader(`{"gdpr":0}`),
 			givenGDPRConfig:  config.GDPR{Enabled: true, DefaultValue: "0"},
 			givenCCPAEnabled: true,
-			expectedPrivacy: privacy.Policies{
-				GDPR: gdprPrivacy.Policy{Signal: "0"},
+			expectedPrivacy: macros.UserSyncPrivacy{
+				GDPR: "0",
 			},
 			expectedRequest: usersync.Request{
 				Privacy: usersyncPrivacy{
 					gdprPermissions: &fakePermissions{},
+					activityRequest: emptyActivityPoliciesRequest,
 				},
 				SyncTypeFilter: usersync.SyncTypeFilter{
 					IFrame:   usersync.NewUniformBidderFilter(usersync.BidderFilterModeInclude),
@@ -852,12 +852,13 @@ func TestCookieSyncParseRequest(t *testing.T) {
 			givenBody:        strings.NewReader(`{}`),
 			givenGDPRConfig:  config.GDPR{Enabled: true, DefaultValue: "0"},
 			givenCCPAEnabled: true,
-			expectedPrivacy: privacy.Policies{
-				GDPR: gdprPrivacy.Policy{Signal: ""},
+			expectedPrivacy: macros.UserSyncPrivacy{
+				GDPR: "",
 			},
 			expectedRequest: usersync.Request{
 				Privacy: usersyncPrivacy{
 					gdprPermissions: &fakePermissions{},
+					activityRequest: emptyActivityPoliciesRequest,
 				},
 				SyncTypeFilter: usersync.SyncTypeFilter{
 					IFrame:   usersync.NewUniformBidderFilter(usersync.BidderFilterModeInclude),
@@ -893,7 +894,7 @@ func TestCookieSyncParseRequest(t *testing.T) {
 					PriorityGroups: [][]string{{"a", "b", "c"}},
 				},
 			},
-			expectedPrivacy: privacy.Policies{},
+			expectedPrivacy: macros.UserSyncPrivacy{},
 			expectedRequest: usersync.Request{
 				Bidders: []string{"a", "b"},
 				Cooperative: usersync.Cooperative{
@@ -903,6 +904,7 @@ func TestCookieSyncParseRequest(t *testing.T) {
 				Limit: 30,
 				Privacy: usersyncPrivacy{
 					gdprPermissions: &fakePermissions{},
+					activityRequest: emptyActivityPoliciesRequest,
 				},
 				SyncTypeFilter: usersync.SyncTypeFilter{
 					IFrame:   usersync.NewUniformBidderFilter(usersync.BidderFilterModeInclude),
@@ -924,7 +926,7 @@ func TestCookieSyncParseRequest(t *testing.T) {
 					PriorityGroups:   [][]string{{"a", "b", "c"}},
 				},
 			},
-			expectedPrivacy: privacy.Policies{},
+			expectedPrivacy: macros.UserSyncPrivacy{},
 			expectedRequest: usersync.Request{
 				Bidders: []string{"a", "b"},
 				Cooperative: usersync.Cooperative{
@@ -934,6 +936,7 @@ func TestCookieSyncParseRequest(t *testing.T) {
 				Limit: 20,
 				Privacy: usersyncPrivacy{
 					gdprPermissions: &fakePermissions{},
+					activityRequest: emptyActivityPoliciesRequest,
 				},
 				SyncTypeFilter: usersync.SyncTypeFilter{
 					IFrame:   usersync.NewUniformBidderFilter(usersync.BidderFilterModeInclude),
@@ -955,7 +958,7 @@ func TestCookieSyncParseRequest(t *testing.T) {
 					PriorityGroups:   [][]string{{"a", "b", "c"}},
 				},
 			},
-			expectedPrivacy: privacy.Policies{},
+			expectedPrivacy: macros.UserSyncPrivacy{},
 			expectedRequest: usersync.Request{
 				Bidders: []string{"a", "b"},
 				Cooperative: usersync.Cooperative{
@@ -965,6 +968,7 @@ func TestCookieSyncParseRequest(t *testing.T) {
 				Limit: 20,
 				Privacy: usersyncPrivacy{
 					gdprPermissions: &fakePermissions{},
+					activityRequest: emptyActivityPoliciesRequest,
 				},
 				SyncTypeFilter: usersync.SyncTypeFilter{
 					IFrame:   usersync.NewUniformBidderFilter(usersync.BidderFilterModeInclude),
@@ -1561,21 +1565,21 @@ func TestCookieSyncHandleResponse(t *testing.T) {
 		Redirect: usersync.NewUniformBidderFilter(usersync.BidderFilterModeInclude),
 	}
 	syncTypeExpected := []usersync.SyncType{usersync.SyncTypeRedirect}
-	privacyPolicies := privacy.Policies{CCPA: ccpa.Policy{Consent: "anyConsent"}}
+	privacyMacros := macros.UserSyncPrivacy{USPrivacy: "anyConsent"}
 
 	// The & in the URL is necessary to test proper JSON encoding.
 	syncA := usersync.Sync{URL: "https://syncA.com/sync?a=1&b=2", Type: usersync.SyncTypeRedirect, SupportCORS: true}
 	syncerA := MockSyncer{}
-	syncerA.On("GetSync", syncTypeExpected, privacyPolicies).Return(syncA, nil).Maybe()
+	syncerA.On("GetSync", syncTypeExpected, privacyMacros).Return(syncA, nil).Maybe()
 
 	// The & in the URL is necessary to test proper JSON encoding.
 	syncB := usersync.Sync{URL: "https://syncB.com/sync?a=1&b=2", Type: usersync.SyncTypeRedirect, SupportCORS: false}
 	syncerB := MockSyncer{}
-	syncerB.On("GetSync", syncTypeExpected, privacyPolicies).Return(syncB, nil).Maybe()
+	syncerB.On("GetSync", syncTypeExpected, privacyMacros).Return(syncB, nil).Maybe()
 
 	syncWithError := usersync.Sync{}
 	syncerWithError := MockSyncer{}
-	syncerWithError.On("GetSync", syncTypeExpected, privacyPolicies).Return(syncWithError, errors.New("anyError")).Maybe()
+	syncerWithError.On("GetSync", syncTypeExpected, privacyMacros).Return(syncWithError, errors.New("anyError")).Maybe()
 
 	testCases := []struct {
 		description         string
@@ -1673,7 +1677,7 @@ func TestCookieSyncHandleResponse(t *testing.T) {
 
 		writer := httptest.NewRecorder()
 		endpoint := cookieSyncEndpoint{pbsAnalytics: &mockAnalytics}
-		endpoint.handleResponse(writer, syncTypeFilter, cookie, privacyPolicies, test.givenSyncersChosen)
+		endpoint.handleResponse(writer, syncTypeFilter, cookie, privacyMacros, test.givenSyncersChosen)
 
 		if assert.Equal(t, writer.Code, http.StatusOK, test.description+":http_status") {
 			assert.Equal(t, writer.Header().Get("Content-Type"), "application/json; charset=utf-8", test.description+":http_header")
@@ -1988,8 +1992,8 @@ func (m *MockSyncer) SupportsType(syncTypes []usersync.SyncType) bool {
 	return args.Bool(0)
 }
 
-func (m *MockSyncer) GetSync(syncTypes []usersync.SyncType, privacyPolicies privacy.Policies) (usersync.Sync, error) {
-	args := m.Called(syncTypes, privacyPolicies)
+func (m *MockSyncer) GetSync(syncTypes []usersync.SyncType, privacyMacros macros.UserSyncPrivacy) (usersync.Sync, error) {
+	args := m.Called(syncTypes, privacyMacros)
 	return args.Get(0).(usersync.Sync), args.Error(1)
 }
 
diff --git a/endpoints/setuid_test.go b/endpoints/setuid_test.go
index 05030cc7..4f37eb77 100644
--- a/endpoints/setuid_test.go
+++ b/endpoints/setuid_test.go
@@ -15,9 +15,9 @@ import (
 	"github.com/prebid/prebid-server/config"
 	"github.com/prebid/prebid-server/errortypes"
 	"github.com/prebid/prebid-server/gdpr"
+	"github.com/prebid/prebid-server/macros"
 	"github.com/prebid/prebid-server/metrics"
 	"github.com/prebid/prebid-server/openrtb_ext"
-	"github.com/prebid/prebid-server/privacy"
 	"github.com/prebid/prebid-server/usersync"
 	"github.com/stretchr/testify/assert"
 
@@ -217,8 +217,7 @@ func TestSetUIDEndpoint(t *testing.T) {
 			description:            "Sets uid for a bidder allowed by GDPR in GPP, throws warning because GDPR legacy values weren't used",
 		},
 		{
-			uri: "/setuid?bidder=pubmatic&uid=123&gdpr=1&gdpr_consent=" +
-				"malformed",
+			uri:                    "/setuid?bidder=pubmatic&uid=123&gdpr=1&gdpr_consent=malformed",
 			syncersBidderNameToKey: map[string]string{"pubmatic": "pubmatic"},
 			gdprAllowsHostCookies:  true,
 			gdprMalformed:          true,
@@ -306,6 +305,26 @@ func TestSetUIDEndpoint(t *testing.T) {
 			expectedHeaders:        map[string]string{"Content-Type": "text/html", "Content-Length": "0"},
 			description:            "Set uid for valid bidder with valid account provided with invalid user sync activity",
 		},
+		{
+			description:            "gppsid-valid",
+			uri:                    "/setuid?bidder=appnexus&uid=123&gpp_sid=100,101", // fake sids to avoid GDPR logic in this test
+			syncersBidderNameToKey: map[string]string{"appnexus": "appnexus"},
+			existingSyncs:          nil,
+			gdprAllowsHostCookies:  true,
+			expectedSyncs:          map[string]string{"appnexus": "123"},
+			expectedStatusCode:     http.StatusOK,
+			expectedHeaders:        map[string]string{"Content-Type": "text/html", "Content-Length": "0"},
+		},
+		{
+			description:            "gppsid-malformed",
+			uri:                    "/setuid?bidder=appnexus&uid=123&gpp_sid=malformed",
+			syncersBidderNameToKey: map[string]string{"appnexus": "appnexus"},
+			existingSyncs:          nil,
+			gdprAllowsHostCookies:  true,
+			expectedSyncs:          nil,
+			expectedStatusCode:     http.StatusBadRequest,
+			expectedBody:           "invalid gpp_sid encoding, must be a csv list of integers",
+		},
 	}
 
 	analytics := analyticsConf.NewPBSAnalytics(&config.Analytics{})
@@ -1476,7 +1495,7 @@ func (s fakeSyncer) SupportsType(syncTypes []usersync.SyncType) bool {
 	return true
 }
 
-func (s fakeSyncer) GetSync(syncTypes []usersync.SyncType, privacyPolicies privacy.Policies) (usersync.Sync, error) {
+func (s fakeSyncer) GetSync(syncTypes []usersync.SyncType, privacyMacros macros.UserSyncPrivacy) (usersync.Sync, error) {
 	return usersync.Sync{}, nil
 }
 
diff --git a/exchange/utils_test.go b/exchange/utils_test.go
index a1ca0a60..ee6dad1a 100644
--- a/exchange/utils_test.go
+++ b/exchange/utils_test.go
@@ -486,7 +486,7 @@ func TestCleanOpenRTBRequests(t *testing.T) {
 			hostSChainNode:    nil,
 			bidderInfo:        config.BidderInfos{},
 		}
-		bidderRequests, _, err := reqSplitter.cleanOpenRTBRequests(context.Background(), test.req, nil, gdpr.SignalNo)
+		bidderRequests, _, err := reqSplitter.cleanOpenRTBRequests(context.Background(), test.req, nil, gdpr.SignalNo, map[string]float64{})
 		if test.hasError {
 			assert.NotNil(t, err, "Error shouldn't be nil")
 		} else {
@@ -552,7 +552,7 @@ func TestCleanOpenRTBRequestsWithFPD(t *testing.T) {
 			bidderInfo:        config.BidderInfos{},
 		}
 
-		bidderRequests, _, err := reqSplitter.cleanOpenRTBRequests(context.Background(), test.req, nil, gdpr.SignalNo)
+		bidderRequests, _, err := reqSplitter.cleanOpenRTBRequests(context.Background(), test.req, nil, gdpr.SignalNo, map[string]float64{})
 		assert.Empty(t, err, "No errors should be returned")
 		for _, bidderRequest := range bidderRequests {
 			bidderName := bidderRequest.BidderName
@@ -867,7 +867,7 @@ func TestCleanOpenRTBRequestsWithBidResponses(t *testing.T) {
 			bidderInfo:        config.BidderInfos{},
 		}
 
-		actualBidderRequests, _, err := reqSplitter.cleanOpenRTBRequests(context.Background(), auctionReq, nil, gdpr.SignalNo)
+		actualBidderRequests, _, err := reqSplitter.cleanOpenRTBRequests(context.Background(), auctionReq, nil, gdpr.SignalNo, map[string]float64{})
 		assert.Empty(t, err, "No errors should be returned")
 		assert.Len(t, actualBidderRequests, len(test.expectedBidderRequests), "result len doesn't match for testCase %s", test.description)
 		for _, actualBidderRequest := range actualBidderRequests {
@@ -1036,7 +1036,7 @@ func TestCleanOpenRTBRequestsCCPA(t *testing.T) {
 			bidderInfo:        config.BidderInfos{},
 		}
 
-		bidderRequests, privacyLabels, errs := reqSplitter.cleanOpenRTBRequests(context.Background(), auctionReq, nil, gdpr.SignalNo)
+		bidderRequests, privacyLabels, errs := reqSplitter.cleanOpenRTBRequests(context.Background(), auctionReq, nil, gdpr.SignalNo, map[string]float64{})
 		result := bidderRequests[0]
 
 		assert.Nil(t, errs)
@@ -1113,7 +1113,7 @@ func TestCleanOpenRTBRequestsCCPAErrors(t *testing.T) {
 			bidderInfo:        config.BidderInfos{},
 		}
 
-		_, _, errs := reqSplitter.cleanOpenRTBRequests(context.Background(), auctionReq, &reqExtStruct, gdpr.SignalNo)
+		_, _, errs := reqSplitter.cleanOpenRTBRequests(context.Background(), auctionReq, &reqExtStruct, gdpr.SignalNo, map[string]float64{})
 
 		assert.ElementsMatch(t, []error{test.expectError}, errs, test.description)
 	}
@@ -1172,7 +1172,7 @@ func TestCleanOpenRTBRequestsCOPPA(t *testing.T) {
 			bidderInfo:        config.BidderInfos{},
 		}
 
-		bidderRequests, privacyLabels, errs := reqSplitter.cleanOpenRTBRequests(context.Background(), auctionReq, nil, gdpr.SignalNo)
+		bidderRequests, privacyLabels, errs := reqSplitter.cleanOpenRTBRequests(context.Background(), auctionReq, nil, gdpr.SignalNo, map[string]float64{})
 		result := bidderRequests[0]
 
 		assert.Nil(t, errs)
@@ -1265,7 +1265,7 @@ func TestCleanOpenRTBRequestsSChain(t *testing.T) {
 			bidderInfo:        config.BidderInfos{},
 		}
 
-		bidderRequests, _, errs := reqSplitter.cleanOpenRTBRequests(context.Background(), auctionReq, extRequest, gdpr.SignalNo)
+		bidderRequests, _, errs := reqSplitter.cleanOpenRTBRequests(context.Background(), auctionReq, extRequest, gdpr.SignalNo, map[string]float64{})
 		if test.hasError == true {
 			assert.NotNil(t, errs)
 			assert.Len(t, bidderRequests, 0)
@@ -1336,7 +1336,7 @@ func TestCleanOpenRTBRequestsBidderParams(t *testing.T) {
 			bidderInfo:        config.BidderInfos{},
 		}
 
-		bidderRequests, _, errs := reqSplitter.cleanOpenRTBRequests(context.Background(), auctionReq, extRequest, gdpr.SignalNo)
+		bidderRequests, _, errs := reqSplitter.cleanOpenRTBRequests(context.Background(), auctionReq, extRequest, gdpr.SignalNo, map[string]float64{})
 		if test.hasError == true {
 			assert.NotNil(t, errs)
 			assert.Len(t, bidderRequests, 0)
@@ -1923,7 +1923,7 @@ func TestCleanOpenRTBRequestsLMT(t *testing.T) {
 			bidderInfo:        config.BidderInfos{},
 		}
 
-		results, privacyLabels, errs := reqSplitter.cleanOpenRTBRequests(context.Background(), auctionReq, nil, gdpr.SignalNo)
+		results, privacyLabels, errs := reqSplitter.cleanOpenRTBRequests(context.Background(), auctionReq, nil, gdpr.SignalNo, map[string]float64{})
 		result := results[0]
 
 		assert.Nil(t, errs)
@@ -2156,7 +2156,7 @@ func TestCleanOpenRTBRequestsGDPR(t *testing.T) {
 			bidderInfo:        config.BidderInfos{},
 		}
 
-		results, privacyLabels, errs := reqSplitter.cleanOpenRTBRequests(context.Background(), auctionReq, nil, gdprDefaultValue)
+		results, privacyLabels, errs := reqSplitter.cleanOpenRTBRequests(context.Background(), auctionReq, nil, gdprDefaultValue, map[string]float64{})
 		result := results[0]
 
 		if test.expectError {
@@ -2257,7 +2257,7 @@ func TestCleanOpenRTBRequestsGDPRBlockBidRequest(t *testing.T) {
 			bidderInfo:        config.BidderInfos{},
 		}
 
-		results, _, errs := reqSplitter.cleanOpenRTBRequests(context.Background(), auctionReq, nil, gdpr.SignalNo)
+		results, _, errs := reqSplitter.cleanOpenRTBRequests(context.Background(), auctionReq, nil, gdpr.SignalNo, map[string]float64{})
 
 		// extract bidder name from each request in the results
 		bidders := []openrtb_ext.BidderName{}
@@ -2344,7 +2344,7 @@ func TestCleanOpenRTBRequestsWithOpenRTBDowngrade(t *testing.T) {
 				hostSChainNode:    nil,
 				bidderInfo:        test.bidderInfos,
 			}
-			bidderRequests, _, err := reqSplitter.cleanOpenRTBRequests(context.Background(), test.req, nil, gdpr.SignalNo)
+			bidderRequests, _, err := reqSplitter.cleanOpenRTBRequests(context.Background(), test.req, nil, gdpr.SignalNo, map[string]float64{})
 			assert.Nil(t, err, "Err should be nil")
 			bidRequest := bidderRequests[0]
 			assert.Equal(t, test.expectRegs, bidRequest.BidRequest.Regs)
@@ -2605,7 +2605,8 @@ func newBidRequest(t *testing.T) *openrtb2.BidRequest {
 			Geo:      &openrtb2.Geo{Lat: 123.456},
 		},
 		Imp: []openrtb2.Imp{{
-			ID: "some-imp-id",
+			BidFloor: 100,
+			ID:       "some-imp-id",
 			Banner: &openrtb2.Banner{
 				Format: []openrtb2.Format{{
 					W: 300,
@@ -3100,7 +3101,7 @@ func TestCleanOpenRTBRequestsSChainMultipleBidders(t *testing.T) {
 		hostSChainNode:    nil,
 		bidderInfo:        config.BidderInfos{},
 	}
-	bidderRequests, _, errs := reqSplitter.cleanOpenRTBRequests(context.Background(), auctionReq, extRequest, gdpr.SignalNo)
+	bidderRequests, _, errs := reqSplitter.cleanOpenRTBRequests(context.Background(), auctionReq, extRequest, gdpr.SignalNo, map[string]float64{})
 
 	assert.Nil(t, errs)
 	assert.Len(t, bidderRequests, 2, "Bid request count is not 2")
@@ -3116,6 +3117,117 @@ func TestCleanOpenRTBRequestsSChainMultipleBidders(t *testing.T) {
 	assert.Equal(t, axonixPrebidSchainsSchain, bidRequestSourceExts["axonix"], "Incorrect axonix bid request schain in source.ext")
 }
 
+func TestCleanOpenRTBRequestsBidAdjustment(t *testing.T) {
+	tcf2Consent := "COzTVhaOzTVhaGvAAAENAiCIAP_AAH_AAAAAAEEUACCKAAA"
+	falseValue := false
+	testCases := []struct {
+		description         string
+		gdprAccountEnabled  *bool
+		gdprHostEnabled     bool
+		gdpr                string
+		gdprConsent         string
+		gdprScrub           bool
+		permissionsError    error
+		gdprDefaultValue    string
+		expectPrivacyLabels metrics.PrivacyLabels
+		expectError         bool
+		bidAdjustmentFactor map[string]float64
+		expectedImp         []openrtb2.Imp
+	}{
+		{
+			description:        "BidFloor Adjustment Done for Appnexus",
+			gdprAccountEnabled: &falseValue,
+			gdprHostEnabled:    true,
+			gdpr:               "1",
+			gdprConsent:        tcf2Consent,
+			gdprScrub:          false,
+			gdprDefaultValue:   "1",
+			expectPrivacyLabels: metrics.PrivacyLabels{
+				GDPREnforced:   false,
+				GDPRTCFVersion: "",
+			},
+			bidAdjustmentFactor: map[string]float64{"appnexus": 0.50},
+			expectedImp: []openrtb2.Imp{{
+				BidFloor: 200,
+				ID:       "some-imp-id",
+				Banner: &openrtb2.Banner{
+					Format: []openrtb2.Format{{
+						W: 300,
+						H: 250,
+					}, {
+						W: 300,
+						H: 600,
+					}},
+				},
+				Ext: json.RawMessage(`{"bidder":{"placementId":1}}`),
+			}},
+		},
+		{
+			description:        "bidAjustement Not provided",
+			gdprAccountEnabled: &falseValue,
+			gdprHostEnabled:    true,
+			gdpr:               "1",
+			gdprConsent:        tcf2Consent,
+			gdprScrub:          false,
+			gdprDefaultValue:   "1",
+			expectPrivacyLabels: metrics.PrivacyLabels{
+				GDPREnforced:   false,
+				GDPRTCFVersion: "",
+			},
+			bidAdjustmentFactor: map[string]float64{},
+			expectedImp: []openrtb2.Imp{{
+				BidFloor: 100,
+				ID:       "some-imp-id",
+				Banner: &openrtb2.Banner{
+					Format: []openrtb2.Format{{
+						W: 300,
+						H: 250,
+					}, {
+						W: 300,
+						H: 600,
+					}},
+				},
+				Ext: json.RawMessage(`{"bidder":{"placementId":1}}`),
+			}},
+		},
+	}
+	for _, test := range testCases {
+		req := newBidRequest(t)
+		accountConfig := config.Account{
+			GDPR: config.AccountGDPR{
+				Enabled: &falseValue,
+			},
+			PriceFloors: config.AccountPriceFloors{
+				AdjustForBidAdjustment: true,
+			},
+		}
+		auctionReq := AuctionRequest{
+			BidRequestWrapper: &openrtb_ext.RequestWrapper{BidRequest: req},
+			UserSyncs:         &emptyUsersync{},
+			Account:           accountConfig,
+		}
+		gdprPermissionsBuilder := fakePermissionsBuilder{
+			permissions: &permissionsMock{
+				allowAllBidders: true,
+				passGeo:         !test.gdprScrub,
+				passID:          !test.gdprScrub,
+				activitiesError: test.permissionsError,
+			},
+		}.Builder
+		reqSplitter := &requestSplitter{
+			bidderToSyncerKey: map[string]string{},
+			me:                &metrics.MetricsEngineMock{},
+			gdprPermsBuilder:  gdprPermissionsBuilder,
+			hostSChainNode:    nil,
+			bidderInfo:        config.BidderInfos{},
+		}
+		results, _, errs := reqSplitter.cleanOpenRTBRequests(context.Background(), auctionReq, nil, gdpr.SignalNo, test.bidAdjustmentFactor)
+		result := results[0]
+		assert.Nil(t, errs)
+		assert.Equal(t, test.expectedImp, result.BidRequest.Imp, test.description)
+	}
+}
+
 func TestApplyFPD(t *testing.T) {
 
 	testCases := []struct {
@@ -3450,7 +3562,7 @@ func TestCleanOpenRTBRequestsFilterBidderRequestExt(t *testing.T) {
 			bidderInfo:        config.BidderInfos{},
 		}
 
-		bidderRequests, _, errs := reqSplitter.cleanOpenRTBRequests(context.Background(), auctionReq, extRequest, gdpr.SignalNo)
+		bidderRequests, _, errs := reqSplitter.cleanOpenRTBRequests(context.Background(), auctionReq, extRequest, gdpr.SignalNo, map[string]float64{})
 		assert.Equal(t, test.wantError, len(errs) != 0, test.desc)
 		sort.Slice(bidderRequests, func(i, j int) bool {
 			return bidderRequests[i].BidderCoreName < bidderRequests[j].BidderCoreName
@@ -4380,7 +4492,7 @@ func TestCleanOpenRTBRequestsActivities(t *testing.T) {
 		}
 
 		t.Run(test.name, func(t *testing.T) {
-			bidderRequests, _, errs := reqSplitter.cleanOpenRTBRequests(context.Background(), auctionReq, nil, gdpr.SignalNo)
+			bidderRequests, _, errs := reqSplitter.cleanOpenRTBRequests(context.Background(), auctionReq, nil, gdpr.SignalNo, map[string]float64{})
 			assert.Empty(t, errs)
 			assert.Len(t, bidderRequests, test.expectedReqNumber)
 
@@ -4440,3 +4552,122 @@ func getTransmitTIDActivityConfig(componentName string, allow bool) config.Accou
 		},
 	}
 }
+
+func TestApplyBidAdjustmentToFloor(t *testing.T) {
+	type args struct {
+		allBidderRequests    []BidderRequest
+		bidAdjustmentFactors map[string]float64
+	}
+	tests := []struct {
+		name                      string
+		args                      args
+		expectedAllBidderRequests []BidderRequest
+	}{
+		{
+			name: " bidAdjustmentFactor is empty",
+			args: args{
+				allBidderRequests: []BidderRequest{
+					{
+						BidRequest: &openrtb2.BidRequest{
+							Imp: []openrtb2.Imp{{BidFloor: 100}, {BidFloor: 150}},
+						},
+						BidderName: openrtb_ext.BidderName("appnexus"),
+					},
+				},
+				bidAdjustmentFactors: map[string]float64{},
+			},
+			expectedAllBidderRequests: []BidderRequest{
+				{
+					BidRequest: &openrtb2.BidRequest{
+						Imp: []openrtb2.Imp{{BidFloor: 100}, {BidFloor: 150}},
+					},
+					BidderName: openrtb_ext.BidderName("appnexus"),
+				},
+			},
+		},
+		{
+			name: "bidAdjustmentFactor not present for request bidder",
+			args: args{
+				allBidderRequests: []BidderRequest{
+					{
+						BidRequest: &openrtb2.BidRequest{
+							Imp: []openrtb2.Imp{{BidFloor: 100}, {BidFloor: 150}},
+						},
+						BidderName: openrtb_ext.BidderName("appnexus"),
+					},
+				},
+				bidAdjustmentFactors: map[string]float64{"pubmatic": 1.0},
+			},
+			expectedAllBidderRequests: []BidderRequest{
+				{
+					BidRequest: &openrtb2.BidRequest{
+						Imp: []openrtb2.Imp{{BidFloor: 100}, {BidFloor: 150}},
+					},
+					BidderName: openrtb_ext.BidderName("appnexus"),
+				},
+			},
+		},
+		{
+			name: "bidAdjustmentFactor present for request bidder",
+			args: args{
+				allBidderRequests: []BidderRequest{
+					{
+						BidRequest: &openrtb2.BidRequest{
+							Imp: []openrtb2.Imp{{BidFloor: 100}, {BidFloor: 150}},
+						},
+						BidderName: openrtb_ext.BidderName("appnexus"),
+					},
+				},
+				bidAdjustmentFactors: map[string]float64{"pubmatic": 1.0, "appnexus": 0.75},
+			},
+			expectedAllBidderRequests: []BidderRequest{
+				{
+					BidRequest: &openrtb2.BidRequest{
+						Imp: []openrtb2.Imp{{BidFloor: 133.33333333333334}, {BidFloor: 200}},
+					},
+					BidderName: openrtb_ext.BidderName("appnexus"),
+				},
+			},
+		},
+		{
+			name: "bidAdjustmentFactor present only for appnexus request bidder",
+			args: args{
+				allBidderRequests: []BidderRequest{
+					{
+						BidRequest: &openrtb2.BidRequest{
+							Imp: []openrtb2.Imp{{BidFloor: 100}, {BidFloor: 150}},
+						},
+						BidderName: openrtb_ext.BidderName("appnexus"),
+					},
+					{
+						BidRequest: &openrtb2.BidRequest{
+							Imp: []openrtb2.Imp{{BidFloor: 100}, {BidFloor: 150}},
+						},
+						BidderName: openrtb_ext.BidderName("pubmatic"),
+					},
+				},
+				bidAdjustmentFactors: map[string]float64{"appnexus": 0.75},
+			},
+			expectedAllBidderRequests: []BidderRequest{
+				{
+					BidRequest: &openrtb2.BidRequest{
+						Imp: []openrtb2.Imp{{BidFloor: 133.33333333333334}, {BidFloor: 200}},
+					},
+					BidderName: openrtb_ext.BidderName("appnexus"),
+				},
+				{
+					BidRequest: &openrtb2.BidRequest{
+						Imp: []openrtb2.Imp{{BidFloor: 100}, {BidFloor: 150}},
+					},
+					BidderName: openrtb_ext.BidderName("pubmatic"),
+				},
+			},
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			applyBidAdjustmentToFloor(tt.args.allBidderRequests, tt.args.bidAdjustmentFactors)
+			assert.Equal(t, tt.expectedAllBidderRequests, tt.args.allBidderRequests, tt.name)
+		})
+	}
+}
diff --git a/macros/macros_test.go b/macros/macros_test.go
index 055624b6..db1b8be9 100644
--- a/macros/macros_test.go
+++ b/macros/macros_test.go
@@ -26,7 +26,7 @@ func TestResolveMacros(t *testing.T) {
 		},
 		{
 			givenTemplate:  endpointTemplate,
-			givenParams:    UserSyncTemplateParams{GDPR: "SomeGDPR", GDPRConsent: "SomeGDPRConsent"},
+			givenParams:    UserSyncPrivacy{GDPR: "SomeGDPR", GDPRConsent: "SomeGDPRConsent"},
 			expectedResult: "",
 			expectedError:  true,
 		},
diff --git a/privacy/activitycontrol_test.go b/privacy/activitycontrol_test.go
index 11a9bdc7..743888df 100644
--- a/privacy/activitycontrol_test.go
+++ b/privacy/activitycontrol_test.go
@@ -4,6 +4,7 @@ import (
 	"testing"
 
 	"github.com/prebid/prebid-server/config"
+	"github.com/prebid/prebid-server/openrtb_ext"
 	"github.com/prebid/prebid-server/util/ptrutil"
 	"github.com/stretchr/testify/assert"
 )
@@ -40,8 +41,8 @@ func TestNewActivityControl(t *testing.T) {
 				ActivityReportAnalytics:          getTestActivityPlan(ActivityAllow),
 				ActivityTransmitUserFPD:          getTestActivityPlan(ActivityAllow),
 				ActivityTransmitPreciseGeo:       getTestActivityPlan(ActivityDeny),
-				ActivityTransmitUniqueRequestIds: getTestActivityPlan(ActivityAllow),
-				ActivityTransmitTids:             getTestActivityPlan(ActivityAllow),
+				ActivityTransmitUniqueRequestIDs: getTestActivityPlan(ActivityAllow),
+				ActivityTransmitTIDs:             getTestActivityPlan(ActivityAllow),
 			}},
 		},
 	}
@@ -128,13 +129,33 @@ func TestActivityControlAllow(t *testing.T) {
 
 	for _, test := range testCases {
 		t.Run(test.name, func(t *testing.T) {
-			actualResult := test.activityControl.Allow(test.activity, test.target)
+			actualResult := test.activityControl.Allow(test.activity, test.target, ActivityRequest{})
 			assert.Equal(t, test.activityResult, actualResult)
 
 		})
 	}
 }
 
+func TestActivityRequest(t *testing.T) {
+	t.Run("empty", func(t *testing.T) {
+		r := ActivityRequest{}
+		assert.False(t, r.IsPolicies())
+		assert.False(t, r.IsBidRequest())
+	})
+
+	t.Run("policies", func(t *testing.T) {
+		r := NewRequestFromPolicies(Policies{})
+		assert.True(t, r.IsPolicies())
+		assert.False(t, r.IsBidRequest())
+	})
+
+	t.Run("request", func(t *testing.T) {
+		r := NewRequestFromBidRequest(openrtb_ext.RequestWrapper{})
+		assert.False(t, r.IsPolicies())
+		assert.True(t, r.IsBidRequest())
+	})
+}
+
 func getTestActivityConfig(allow bool) config.Activity {
 	return config.Activity{
 		Default: ptrutil.ToPtr(true),
diff --git a/privacy/rule_condition_test.go b/privacy/rule_condition_test.go
index 3a09b365..bb1d81c0 100644
--- a/privacy/rule_condition_test.go
+++ b/privacy/rule_condition_test.go
@@ -3,6 +3,8 @@ package privacy
 import (
 	"testing"
 
+	"github.com/prebid/openrtb/v19/openrtb2"
+	"github.com/prebid/prebid-server/openrtb_ext"
 	"github.com/stretchr/testify/assert"
 )
 
@@ -43,6 +45,15 @@ func TestComponentEnforcementRuleEvaluate(t *testing.T) {
 			target:         Component{Type: "bidder", Name: "bidderB"},
 			activityResult: ActivityAbstain,
 		},
+		{
+			name: "abstain_gppsid",
+			componentRule: ConditionRule{
+				result: ActivityAllow,
+				gppSID: []int8{1},
+			},
+			target:         Component{Type: "bidder", Name: "bidderB"},
+			activityResult: ActivityAbstain,
+		},
 		{
 			name: "activity_is_not_allowed_componentName_only",
 			componentRule: ConditionRule{
@@ -81,9 +92,187 @@ func TestComponentEnforcementRuleEvaluate(t *testing.T) {
 
 	for _, test := range testCases {
 		t.Run(test.name, func(t *testing.T) {
-			actualResult := test.componentRule.Evaluate(test.target)
+			actualResult := test.componentRule.Evaluate(test.target, ActivityRequest{})
 			assert.Equal(t, test.activityResult, actualResult)
+		})
+	}
+}
+
+func TestEvaluateGPPSID(t *testing.T) {
+	testCases := []struct {
+		name         string
+		sidCondition []int8
+		sidRequest   []int8
+		expected     bool
+	}{
+		{
+			name:         "condition-nil-request-nil",
+			sidCondition: nil,
+			sidRequest:   nil,
+			expected:     true,
+		},
+		{
+			name:         "condition-empty-request-nil",
+			sidCondition: []int8{},
+			sidRequest:   nil,
+			expected:     true,
+		},
+		{
+			name:         "condition-nil-request-empty",
+			sidCondition: nil,
+			sidRequest:   []int8{},
+			expected:     true,
+		},
+		{
+			name:         "condition-empty-request-empty",
+			sidCondition: []int8{},
+			sidRequest:   []int8{},
+			expected:     true,
+		},
+		{
+			name:         "condition-one-request-nil",
+			sidCondition: []int8{1},
+			sidRequest:   nil,
+			expected:     false,
+		},
+		{
+			name:         "condition-many-request-nil",
+			sidCondition: []int8{1, 2},
+			sidRequest:   nil,
+			expected:     false,
+		},
+		{
+			name:         "condition-one-request-empty",
+			sidCondition: []int8{1},
+			sidRequest:   []int8{},
+			expected:     false,
+		},
+		{
+			name:         "condition-many-request-empty",
+			sidCondition: []int8{1, 2},
+			sidRequest:   []int8{},
+			expected:     false,
+		},
+		{
+			name:         "condition-one-request-one-match",
+			sidCondition: []int8{1},
+			sidRequest:   []int8{1},
+			expected:     true,
+		},
+		{
+			name:         "condition-one-request-one-nomatch",
+			sidCondition: []int8{1},
+			sidRequest:   []int8{2},
+			expected:     false,
+		},
+		{
+			name:         "condition-one-request-many-match",
+			sidCondition: []int8{1},
+			sidRequest:   []int8{1, 2},
+			expected:     true,
+		},
+		{
+			name:         "condition-one-request-many-nomatch",
+			sidCondition: []int8{3},
+			sidRequest:   []int8{1, 2},
+			expected:     false,
+		},
+		{
+			name:         "condition-nil-request-one",
+			sidCondition: nil,
+			sidRequest:   []int8{1},
+			expected:     true,
+		},
+		{
+			name:         "condition-nil-request-many",
+			sidCondition: nil,
+			sidRequest:   []int8{1, 2},
+			expected:     true,
+		},
+		{
+			name:         "condition-empty-request-one",
+			sidCondition: []int8{},
+			sidRequest:   []int8{1},
+			expected:     true,
+		},
+		{
+			name:         "condition-empty-request-many",
+			sidCondition: []int8{},
+			sidRequest:   []int8{1, 2},
+			expected:     true,
+		},
+		{
+			name:         "condition-many-request-one-match",
+			sidCondition: []int8{1, 2},
+			sidRequest:   []int8{1},
+			expected:     true,
+		},
+		{
+			name:         "condition-many-request-one-nomatch",
+			sidCondition: []int8{1, 2},
+			sidRequest:   []int8{3},
+			expected:     false,
+		},
+		{
+			name:         "condition-many-request-many-match",
+			sidCondition: []int8{1, 2},
+			sidRequest:   []int8{1, 2},
+			expected:     true,
+		},
+		{
+			name:         "condition-many-request-many-nomatch",
+			sidCondition: []int8{1, 2},
+			sidRequest:   []int8{3, 4},
+			expected:     false,
+		},
+		{
+			name:         "condition-many-request-many-mixed",
+			sidCondition: []int8{1, 2},
+			sidRequest:   []int8{2, 3},
+			expected:     true,
+		},
+	}
+
+	for _, test := range testCases {
+		t.Run(test.name, func(t *testing.T) {
+			actualResult := evaluateGPPSID(test.sidCondition, NewRequestFromPolicies(Policies{GPPSID: test.sidRequest}))
+			assert.Equal(t, test.expected, actualResult)
+		})
+	}
+}
 
+func TestGetGPPSID(t *testing.T) {
+	testCases := []struct {
+		name     string
+		request  ActivityRequest
+		expected []int8
+	}{
+		{
+			name:     "empty",
+			request:  ActivityRequest{},
+			expected: nil,
+		},
+		{
+			name:     "policies",
+			request:  ActivityRequest{policies: &Policies{GPPSID: []int8{1}}},
+			expected: []int8{1},
+		},
+		{
+			name:     "request-regs",
+			request:  ActivityRequest{bidRequest: &openrtb_ext.RequestWrapper{BidRequest: &openrtb2.BidRequest{Regs: &openrtb2.Regs{GPPSID: []int8{1}}}}},
+			expected: []int8{1},
+		},
+		{
+			name:     "request-regs-nil",
+			request:  ActivityRequest{bidRequest: &openrtb_ext.RequestWrapper{BidRequest: &openrtb2.BidRequest{Regs: nil}}},
+			expected: nil,
+		},
+	}
+
+	for _, test := range testCases {
+		t.Run(test.name, func(t *testing.T) {
+			actualResult := getGPPSID(test.request)
+			assert.Equal(t, test.expected, actualResult)
 		})
 	}
 }
diff --git a/usersync/chooser_test.go b/usersync/chooser_test.go
index adc217ae..4f38002c 100644
--- a/usersync/chooser_test.go
+++ b/usersync/chooser_test.go
@@ -1,11 +1,12 @@
 package usersync
 
 import (
-	"github.com/prebid/prebid-server/privacy"
-	"github.com/stretchr/testify/assert"
-	"github.com/stretchr/testify/mock"
 	"testing"
 	"time"
+
+	"github.com/prebid/prebid-server/macros"
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/mock"
 )
 
 func TestNewChooser(t *testing.T) {
@@ -376,7 +377,7 @@ func (s fakeSyncer) SupportsType(syncTypes []SyncType) bool {
 	return false
 }
 
-func (fakeSyncer) GetSync(syncTypes []SyncType, privacyPolicies privacy.Policies) (Sync, error) {
+func (fakeSyncer) GetSync([]SyncType, macros.UserSyncPrivacy) (Sync, error) {
 	return Sync{}, nil
 }
 
diff --git a/usersync/syncer_test.go b/usersync/syncer_test.go
index c309b899..72167add 100644
--- a/usersync/syncer_test.go
+++ b/usersync/syncer_test.go
@@ -6,9 +6,6 @@ import (
 
 	"github.com/prebid/prebid-server/config"
 	"github.com/prebid/prebid-server/macros"
-	"github.com/prebid/prebid-server/privacy"
-	"github.com/prebid/prebid-server/privacy/ccpa"
-	"github.com/prebid/prebid-server/privacy/gdpr"
 	"github.com/stretchr/testify/assert"
 )
 
@@ -16,7 +13,7 @@ func TestNewSyncer(t *testing.T) {
 	var (
 		supportCORS      = true
 		hostConfig       = config.UserSync{ExternalURL: "http://host.com", RedirectURL: "{{.ExternalURL}}/host"}
-		macroValues      = macros.UserSyncTemplateParams{GDPR: "A", GDPRConsent: "B", USPrivacy: "C"}
+		macroValues      = macros.UserSyncPrivacy{GDPR: "A", GDPRConsent: "B", USPrivacy: "C"}
 		iframeConfig     = &config.SyncerEndpoint{URL: "https://bidder.com/iframe?redirect={{.RedirectURL}}"}
 		redirectConfig   = &config.SyncerEndpoint{URL: "https://bidder.com/redirect?redirect={{.RedirectURL}}"}
 		errParseConfig   = &config.SyncerEndpoint{URL: "{{malformed}}"}
@@ -186,7 +183,7 @@ func TestBuildTemplate(t *testing.T) {
 		key           = "anyKey"
 		syncTypeValue = "x"
 		hostConfig    = config.UserSync{ExternalURL: "http://host.com", RedirectURL: "{{.ExternalURL}}/host"}
-		macroValues   = macros.UserSyncTemplateParams{GDPR: "A", GDPRConsent: "B", USPrivacy: "C", GPP: "D", GPPSID: "1"}
+		macroValues   = macros.UserSyncPrivacy{GDPR: "A", GDPRConsent: "B", USPrivacy: "C", GPP: "D", GPPSID: "1"}
 	)
 
 	testCases := []struct {
@@ -451,7 +448,7 @@ func TestValidateTemplate(t *testing.T) {
 		{
 			description:   "Contains Unrecognized Macro",
 			given:         template.Must(template.New("test").Parse("invalid:{{.DoesNotExist}}")),
-			expectedError: "template: test:1:10: executing \"test\" at <.DoesNotExist>: can't evaluate field DoesNotExist in type macros.UserSyncTemplateParams",
+			expectedError: "template: test:1:10: executing \"test\" at <.DoesNotExist>: can't evaluate field DoesNotExist in type macros.UserSyncPrivacy",
 		},
 		{
 			description:   "Not A Url",
@@ -636,45 +633,45 @@ func TestSyncerGetSync(t *testing.T) {
 	)
 
 	testCases := []struct {
-		description          string
-		givenSyncer          standardSyncer
-		givenSyncTypes       []SyncType
-		givenPrivacyPolicies privacy.Policies
-		expectedError        string
-		expectedSync         Sync
+		description    string
+		givenSyncer    standardSyncer
+		givenSyncTypes []SyncType
+		givenMacros    macros.UserSyncPrivacy
+		expectedError  string
+		expectedSync   Sync
 	}{
 		{
-			description:          "No Sync Types",
-			givenSyncer:          standardSyncer{iframe: iframeTemplate, redirect: redirectTemplate},
-			givenSyncTypes:       []SyncType{},
-			givenPrivacyPolicies: privacy.Policies{GDPR: gdpr.Policy{Signal: "A", Consent: "B"}, CCPA: ccpa.Policy{Consent: "C"}},
-			expectedError:        "no sync types provided",
+			description:    "No Sync Types",
+			givenSyncer:    standardSyncer{iframe: iframeTemplate, redirect: redirectTemplate},
+			givenSyncTypes: []SyncType{},
+			givenMacros:    macros.UserSyncPrivacy{GDPR: "A", GDPRConsent: "B", USPrivacy: "C"},
+			expectedError:  "no sync types provided",
 		},
 		{
-			description:          "IFrame",
-			givenSyncer:          standardSyncer{iframe: iframeTemplate, redirect: redirectTemplate},
-			givenSyncTypes:       []SyncType{SyncTypeIFrame},
-			givenPrivacyPolicies: privacy.Policies{GDPR: gdpr.Policy{Signal: "A", Consent: "B"}, CCPA: ccpa.Policy{Consent: "C"}},
-			expectedSync:         Sync{URL: "iframe,gdpr:A,gdprconsent:B,ccpa:C", Type: SyncTypeIFrame, SupportCORS: false},
+			description:    "IFrame",
+			givenSyncer:    standardSyncer{iframe: iframeTemplate, redirect: redirectTemplate},
+			givenSyncTypes: []SyncType{SyncTypeIFrame},
+			givenMacros:    macros.UserSyncPrivacy{GDPR: "A", GDPRConsent: "B", USPrivacy: "C"},
+			expectedSync:   Sync{URL: "iframe,gdpr:A,gdprconsent:B,ccpa:C", Type: SyncTypeIFrame, SupportCORS: false},
 		},
 		{
-			description:          "Redirect",
-			givenSyncer:          standardSyncer{iframe: iframeTemplate, redirect: redirectTemplate},
-			givenSyncTypes:       []SyncType{SyncTypeRedirect},
-			givenPrivacyPolicies: privacy.Policies{GDPR: gdpr.Policy{Signal: "A", Consent: "B"}, CCPA: ccpa.Policy{Consent: "C"}},
-			expectedSync:         Sync{URL: "redirect,gdpr:A,gdprconsent:B,ccpa:C", Type: SyncTypeRedirect, SupportCORS: false},
+			description:    "Redirect",
+			givenSyncer:    standardSyncer{iframe: iframeTemplate, redirect: redirectTemplate},
+			givenSyncTypes: []SyncType{SyncTypeRedirect},
+			givenMacros:    macros.UserSyncPrivacy{GDPR: "A", GDPRConsent: "B", USPrivacy: "C"},
+			expectedSync:   Sync{URL: "redirect,gdpr:A,gdprconsent:B,ccpa:C", Type: SyncTypeRedirect, SupportCORS: false},
 		},
 		{
-			description:          "Macro Error",
-			givenSyncer:          standardSyncer{iframe: malformedTemplate},
-			givenSyncTypes:       []SyncType{SyncTypeIFrame},
-			givenPrivacyPolicies: privacy.Policies{GDPR: gdpr.Policy{Signal: "A", Consent: "B"}, CCPA: ccpa.Policy{Consent: "C"}},
-			expectedError:        "template: test:1:20: executing \"test\" at <.DoesNotExist>: can't evaluate field DoesNotExist in type macros.UserSyncTemplateParams",
+			description:    "Macro Error",
+			givenSyncer:    standardSyncer{iframe: malformedTemplate},
+			givenSyncTypes: []SyncType{SyncTypeIFrame},
+			givenMacros:    macros.UserSyncPrivacy{GDPR: "A", GDPRConsent: "B", USPrivacy: "C"},
+			expectedError:  "template: test:1:20: executing \"test\" at <.DoesNotExist>: can't evaluate field DoesNotExist in type macros.UserSyncPrivacy",
 		},
 	}
 
 	for _, test := range testCases {
-		result, err := test.givenSyncer.GetSync(test.givenSyncTypes, test.givenPrivacyPolicies)
+		result, err := test.givenSyncer.GetSync(test.givenSyncTypes, test.givenMacros)
 
 		if test.expectedError == "" {
 			assert.NoError(t, err, test.description+":err")
diff --git a/usersync/syncersbuilder_test.go b/usersync/syncersbuilder_test.go
index fbc18639..15c53dba 100644
--- a/usersync/syncersbuilder_test.go
+++ b/usersync/syncersbuilder_test.go
@@ -5,7 +5,7 @@ import (
 	"testing"
 
 	"github.com/prebid/prebid-server/config"
-	"github.com/prebid/prebid-server/privacy"
+	"github.com/prebid/prebid-server/macros"
 	"github.com/stretchr/testify/assert"
 )
 
@@ -160,7 +160,7 @@ func TestBuildSyncers(t *testing.T) {
 			assert.Empty(t, errs, test.description+":err")
 			resultRenderedIFrameURLS := map[string]string{}
 			for k, v := range result {
-				iframeRendered, err := v.GetSync([]SyncType{SyncTypeIFrame}, privacy.Policies{})
+				iframeRendered, err := v.GetSync([]SyncType{SyncTypeIFrame}, macros.UserSyncPrivacy{})
 				if assert.NoError(t, err, test.description+"key:%s,:iframe_render", k) {
 					resultRenderedIFrameURLS[k] = iframeRendered.URL
 				}