Repository: prebid/prebid-server
Pull request #3060 (2023-08-30 13:26:54)

Sovrn: Adapter to accept ad unit codes

github-actions[bot]: ## Code coverage summary 
Note: 
- Prebid team doesn't anticipate tests covering code paths that might result in marshal and unmarshal errors 
- Coverage summary encompasses all commits leading up to the latest one, bdb896701276b4a382285694116f5f99d455d221 
#### sovrn 
Refer [here](https://htmlpreview.github.io/?https://github.com/prebid/prebid-server/coverage-preview/.github/preview/6025527614_1693402086/sovrn.html) for heat map coverage report 
``` 
github.com/prebid/prebid-server/adapters/sovrn/sovrn.go:28:	MakeRequests		84.3%
github.com/prebid/prebid-server/adapters/sovrn/sovrn.go:133:	addHeaderIfNonEmpty	100.0%
github.com/prebid/prebid-server/adapters/sovrn/sovrn.go:139:	MakeBids		84.0%
github.com/prebid/prebid-server/adapters/sovrn/sovrn.go:193:	getTagId		100.0%
github.com/prebid/prebid-server/adapters/sovrn/sovrn.go:201:	getImpIdx		100.0%
github.com/prebid/prebid-server/adapters/sovrn/sovrn.go:214:	Builder			100.0%
total:								(statements)		86.2%

 ``` 


Patch:
diff --git a/account/account.go b/account/account.go
index 052d0199..156ad5e6 100644
--- a/account/account.go
+++ b/account/account.go
@@ -4,7 +4,6 @@ import (
 	"context"
 	"encoding/json"
 	"fmt"
-
 	"github.com/buger/jsonparser"
 	"github.com/prebid/go-gdpr/consentconstants"
 	"github.com/prebid/prebid-server/config"
@@ -12,6 +11,7 @@ import (
 	"github.com/prebid/prebid-server/metrics"
 	"github.com/prebid/prebid-server/openrtb_ext"
 	"github.com/prebid/prebid-server/stored_requests"
+	"github.com/prebid/prebid-server/util/iputil"
 	jsonpatch "gopkg.in/evanphx/json-patch.v4"
 )
 
@@ -103,6 +103,14 @@ func GetAccount(ctx context.Context, cfg *config.Configuration, fetcher stored_r
 		return nil, errs
 	}
 
+	if ipV6Err := account.Privacy.IPv6Config.Validate(nil); len(ipV6Err) > 0 {
+		account.Privacy.IPv6Config.AnonKeepBits = iputil.IPv6DefaultMaskingBitSize
+	}
+
+	if ipV4Err := account.Privacy.IPv4Config.Validate(nil); len(ipV4Err) > 0 {
+		account.Privacy.IPv4Config.AnonKeepBits = iputil.IPv4DefaultMaskingBitSize
+	}
+
 	// set the value of events.enabled field based on deprecated events_enabled field and ensure backward compatibility
 	deprecateEventsEnabledField(account)
 
diff --git a/adapters/datablocks/datablocks.go b/adapters/datablocks/datablocks.go
index 8929c848..a39721a4 100644
--- a/adapters/datablocks/datablocks.go
+++ b/adapters/datablocks/datablocks.go
@@ -27,7 +27,7 @@ func (a *DatablocksAdapter) MakeRequests(request *openrtb2.BidRequest, reqInfo *
 		"Accept":       {"application/json"},
 	}
 
-	// Pull the host and source ID info from the bidder params.
+	// Pull the source ID info from the bidder params.
 	reqImps, err := splitImpressions(request.Imp)
 
 	if err != nil {
@@ -45,7 +45,7 @@ func (a *DatablocksAdapter) MakeRequests(request *openrtb2.BidRequest, reqInfo *
 			continue
 		}
 
-		urlParams := macros.EndpointTemplateParams{Host: reqExt.Host, SourceId: strconv.Itoa(reqExt.SourceId)}
+		urlParams := macros.EndpointTemplateParams{SourceId: strconv.Itoa(reqExt.SourceId)}
 		url, err := macros.ResolveMacros(a.EndpointTemplate, urlParams)
 
 		if err != nil {
@@ -137,7 +137,7 @@ func getBidderParams(imp *openrtb2.Imp) (*openrtb_ext.ExtImpDatablocks, error) {
 	var datablocksExt openrtb_ext.ExtImpDatablocks
 	if err := json.Unmarshal(bidderExt.Bidder, &datablocksExt); err != nil {
 		return nil, &errortypes.BadInput{
-			Message: fmt.Sprintf("Cannot Resolve host or sourceId: %s", err.Error()),
+			Message: fmt.Sprintf("Cannot Resolve sourceId: %s", err.Error()),
 		}
 	}
 
@@ -147,12 +147,6 @@ func getBidderParams(imp *openrtb2.Imp) (*openrtb_ext.ExtImpDatablocks, error) {
 		}
 	}
 
-	if len(datablocksExt.Host) < 1 {
-		return nil, &errortypes.BadInput{
-			Message: "Invalid/Missing Host",
-		}
-	}
-
 	return &datablocksExt, nil
 }
 
diff --git a/adapters/huaweiads/huaweiads.go b/adapters/huaweiads/huaweiads.go
index 071f0761..2b7c3c25 100644
--- a/adapters/huaweiads/huaweiads.go
+++ b/adapters/huaweiads/huaweiads.go
@@ -185,6 +185,7 @@ type metaData struct {
 	ApkInfo     apkInfo     `json:"apkInfo"`
 	Duration    int64       `json:"duration"`
 	MediaFile   mediaFile   `json:"mediaFile"`
+	Cta         string      `json:"cta"`
 }
 
 type imageInfo struct {
@@ -535,8 +536,7 @@ func getNativeFormat(adslot30 *adslot30, openRTBImp *openrtb2.Imp) error {
 	// only compute the main image number, type = native1.ImageAssetTypeMain
 	var numMainImage = 0
 	var numVideo = 0
-	var width int64
-	var height int64
+
 	for _, asset := range nativePayload.Assets {
 		// Only one of the {title,img,video,data} objects should be present in each object.
 		if asset.Video != nil {
@@ -547,19 +547,10 @@ func getNativeFormat(adslot30 *adslot30, openRTBImp *openrtb2.Imp) error {
 		if asset.Img != nil {
 			if asset.Img.Type == native1.ImageAssetTypeMain {
 				numMainImage++
-				if asset.Img.H != 0 && asset.Img.W != 0 {
-					width = asset.Img.W
-					height = asset.Img.H
-				} else if asset.Img.WMin != 0 && asset.Img.HMin != 0 {
-					width = asset.Img.WMin
-					height = asset.Img.HMin
-				}
 			}
 			continue
 		}
 	}
-	adslot30.W = width
-	adslot30.H = height
 
 	var detailedCreativeTypeList = make([]string, 0, 2)
 	if numVideo >= 1 {
@@ -1169,6 +1160,11 @@ func (a *adapter) extractAdmNative(adType int32, content *content, bidType openr
 				dataObject.Label = "desc"
 				dataObject.Value = getDecodeValue(content.MetaData.Description)
 			}
+
+			if asset.Data.Type == native1.DataAssetTypeCTAText {
+				dataObject.Type = native1.DataAssetTypeCTAText
+				dataObject.Value = getDecodeValue(content.MetaData.Cta)
+			}
 			responseAsset.Data = &dataObject
 		}
 		var id = asset.ID
diff --git a/adapters/response.go b/adapters/response.go
index 080f4627..8b7d59bd 100644
--- a/adapters/response.go
+++ b/adapters/response.go
@@ -15,7 +15,9 @@ func CheckResponseStatusCodeForErrors(response *ResponseData) error {
 	}
 
 	if response.StatusCode != http.StatusOK {
-		return fmt.Errorf("Unexpected status code: %d. Run with request.debug = 1 for more info", response.StatusCode)
+		return &errortypes.BadServerResponse{
+			Message: fmt.Sprintf("Unexpected status code: %d. Run with request.debug = 1 for more info", response.StatusCode),
+		}
 	}
 
 	return nil
diff --git a/adapters/sovrn/sovrn.go b/adapters/sovrn/sovrn.go
index 24022490..405e5ebf 100644
--- a/adapters/sovrn/sovrn.go
+++ b/adapters/sovrn/sovrn.go
@@ -20,6 +20,11 @@ type SovrnAdapter struct {
 	URI string
 }
 
+type sovrnImpExt struct {
+	Bidder     openrtb_ext.ExtImpSovrn `json:"bidder"`
+	AdUnitCode string                  `json:"adunitcode,omitempty"`
+}
+
 func (s *SovrnAdapter) MakeRequests(request *openrtb2.BidRequest, reqInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) {
 	headers := http.Header{}
 	headers.Add("Content-Type", "application/json")
@@ -74,6 +79,20 @@ func (s *SovrnAdapter) MakeRequests(request *openrtb2.BidRequest, reqInfo *adapt
 			imp.BidFloor = sovrnExt.BidFloor
 		}
 
+		var impExtBuffer []byte
+		impExtBuffer, err = json.Marshal(&sovrnImpExt{
+			Bidder:     sovrnExt,
+			AdUnitCode: sovrnExt.AdUnitCode,
+		})
+		if err != nil {
+			errs = append(errs, &errortypes.BadInput{
+				Message: err.Error(),
+			})
+			continue
+		}
+
+		imp.Ext = impExtBuffer
+
 		// Validate video params if appropriate
 		video := imp.Video
 		if video != nil {
diff --git a/adapters/undertone/undertone.go b/adapters/undertone/undertone.go
index 62934fa5..7f8dde35 100644
--- a/adapters/undertone/undertone.go
+++ b/adapters/undertone/undertone.go
@@ -26,6 +26,11 @@ type undertoneParams struct {
 	Version string `json:"version"`
 }
 
+type impExt struct {
+	Bidder *openrtb_ext.ExtImpUndertone `json:"bidder,omitempty"`
+	Gpid   string                       `json:"gpid,omitempty"`
+}
+
 func Builder(bidderName openrtb_ext.BidderName, config config.Adapter, server config.Server) (adapters.Bidder, error) {
 	bidder := &adapter{
 		endpoint: config.Endpoint,
@@ -158,24 +163,29 @@ func getImpsAndPublisherId(bidRequest *openrtb2.BidRequest) ([]openrtb2.Imp, int
 	var validImps []openrtb2.Imp
 
 	for _, imp := range bidRequest.Imp {
-		var extImpBidder adapters.ExtImpBidder
-		if err := json.Unmarshal(imp.Ext, &extImpBidder); err != nil {
-			errs = append(errs, getInvalidImpErr(imp.ID, err))
-			continue
-		}
-
-		var extImpUndertone openrtb_ext.ExtImpUndertone
-		if err := json.Unmarshal(extImpBidder.Bidder, &extImpUndertone); err != nil {
+		var ext impExt
+		if err := json.Unmarshal(imp.Ext, &ext); err != nil {
 			errs = append(errs, getInvalidImpErr(imp.ID, err))
 			continue
 		}
 
 		if publisherId == 0 {
-			publisherId = extImpUndertone.PublisherID
+			publisherId = ext.Bidder.PublisherID
 		}
 
-		imp.TagID = strconv.Itoa(extImpUndertone.PlacementID)
+		imp.TagID = strconv.Itoa(ext.Bidder.PlacementID)
 		imp.Ext = nil
+
+		if ext.Gpid != "" {
+			ext.Bidder = nil
+			impExtJson, err := json.Marshal(&ext)
+			if err != nil {
+				errs = append(errs, getInvalidImpErr(imp.ID, err))
+				continue
+			}
+			imp.Ext = impExtJson
+		}
+
 		validImps = append(validImps, imp)
 	}
 
diff --git a/config/account.go b/config/account.go
index 02040211..038a8daa 100644
--- a/config/account.go
+++ b/config/account.go
@@ -8,6 +8,7 @@ import (
 
 	"github.com/prebid/go-gdpr/consentconstants"
 	"github.com/prebid/prebid-server/openrtb_ext"
+	"github.com/prebid/prebid-server/util/iputil"
 )
 
 // ChannelType enumerates the values of integrations Prebid Server can configure for an account
@@ -40,7 +41,7 @@ type Account struct {
 	Validations             Validations                                 `mapstructure:"validations" json:"validations"`
 	DefaultBidLimit         int                                         `mapstructure:"default_bid_limit" json:"default_bid_limit"`
 	BidAdjustments          *openrtb_ext.ExtRequestPrebidBidAdjustments `mapstructure:"bidadjustments" json:"bidadjustments"`
-	Privacy                 *AccountPrivacy                             `mapstructure:"privacy" json:"privacy"`
+	Privacy                 AccountPrivacy                              `mapstructure:"privacy" json:"privacy"`
 }
 
 // CookieSync represents the account-level defaults for the cookie sync endpoint.
@@ -295,5 +296,31 @@ func (a *AccountChannel) IsSet() bool {
 }
 
 type AccountPrivacy struct {
-	AllowActivities AllowActivities `mapstructure:"allowactivities" json:"allowactivities"`
+	AllowActivities *AllowActivities `mapstructure:"allowactivities" json:"allowactivities"`
+	IPv6Config      IPv6             `mapstructure:"ipv6" json:"ipv6"`
+	IPv4Config      IPv4             `mapstructure:"ipv4" json:"ipv4"`
+}
+
+type IPv6 struct {
+	AnonKeepBits int `mapstructure:"anon_keep_bits" json:"anon_keep_bits"`
+}
+
+type IPv4 struct {
+	AnonKeepBits int `mapstructure:"anon_keep_bits" json:"anon_keep_bits"`
+}
+
+func (ip *IPv6) Validate(errs []error) []error {
+	if ip.AnonKeepBits > iputil.IPv6BitSize || ip.AnonKeepBits < 0 {
+		err := fmt.Errorf("bits cannot exceed %d in ipv6 address, or be less than 0", iputil.IPv6BitSize)
+		errs = append(errs, err)
+	}
+	return errs
+}
+
+func (ip *IPv4) Validate(errs []error) []error {
+	if ip.AnonKeepBits > iputil.IPv4BitSize || ip.AnonKeepBits < 0 {
+		err := fmt.Errorf("bits cannot exceed %d in ipv4 address, or be less than 0", iputil.IPv4BitSize)
+		errs = append(errs, err)
+	}
+	return errs
 }
diff --git a/config/bidderinfo.go b/config/bidderinfo.go
index df9e9d5a..96d6fd15 100644
--- a/config/bidderinfo.go
+++ b/config/bidderinfo.go
@@ -241,17 +241,11 @@ func processBidderInfos(reader InfoReader, normalizeBidderName func(string) (ope
 	for fileName, data := range bidderConfigs {
 		bidderName := strings.Split(fileName, ".")
 		if len(bidderName) == 2 && bidderName[1] == "yaml" {
-			normalizedBidderName, bidderNameExists := normalizeBidderName(bidderName[0])
-			if !bidderNameExists {
-				return nil, fmt.Errorf("error parsing config for bidder %s: unknown bidder", fileName)
-			}
 			info := BidderInfo{}
 			if err := yaml.Unmarshal(data, &info); err != nil {
 				return nil, fmt.Errorf("error parsing config for bidder %s: %v", fileName, err)
 			}
 
-			bidderInfos[string(normalizedBidderName)] = info
-
 			//need to maintain nullable fields from BidderInfo struct into bidderInfoNullableFields
 			//to handle the default values in aliases yaml
 			if len(info.AliasOf) > 0 {
@@ -260,7 +254,25 @@ func processBidderInfos(reader InfoReader, normalizeBidderName func(string) (ope
 					return nil, fmt.Errorf("error parsing config for aliased bidder %s: %v", fileName, err)
 				}
 
+				//required for CoreBidderNames function to also return aliasBiddernames
+				if err := openrtb_ext.SetAliasBidderName(bidderName[0], openrtb_ext.BidderName(info.AliasOf)); err != nil {
+					return nil, err
+				}
+
+				normalizedBidderName, bidderNameExists := normalizeBidderName(bidderName[0])
+				if !bidderNameExists {
+					return nil, fmt.Errorf("error parsing config for an alias %s: unknown bidder", fileName)
+				}
+
 				aliasNillableFieldsByBidder[string(normalizedBidderName)] = aliasFields
+				bidderInfos[string(normalizedBidderName)] = info
+			} else {
+				normalizedBidderName, bidderNameExists := normalizeBidderName(bidderName[0])
+				if !bidderNameExists {
+					return nil, fmt.Errorf("error parsing config for bidder %s: unknown bidder", fileName)
+				}
+
+				bidderInfos[string(normalizedBidderName)] = info
 			}
 		}
 	}
@@ -276,8 +288,7 @@ func processBidderAliases(aliasNillableFieldsByBidder map[string]aliasNillableFi
 		if err := validateAliases(aliasBidderInfo, bidderInfos, bidderName); err != nil {
 			return nil, err
 		}
-		//required for CoreBidderNames function to also return aliasBiddernames
-		openrtb_ext.SetAliasBidderName(openrtb_ext.BidderName(string(bidderName)))
+
 		parentBidderInfo := bidderInfos[aliasBidderInfo.AliasOf]
 		if aliasBidderInfo.AppSecret == "" {
 			aliasBidderInfo.AppSecret = parentBidderInfo.AppSecret
diff --git a/config/config.go b/config/config.go
index 2b539c57..e564c34a 100644
--- a/config/config.go
+++ b/config/config.go
@@ -149,12 +149,11 @@ func (cfg *Configuration) validate(v *viper.Viper) []error {
 		errs = append(errs, errors.New("account_defaults.Events.VASTEvents has no effect as the feature is under development."))
 	}
 
-	if cfg.AccountDefaults.Privacy != nil {
-		glog.Warning("account_defaults.Privacy has no effect as the feature is under development.")
-	}
-
 	errs = cfg.Experiment.validate(errs)
 	errs = cfg.BidderInfos.validate(errs)
+	errs = cfg.AccountDefaults.Privacy.IPv6Config.Validate(errs)
+	errs = cfg.AccountDefaults.Privacy.IPv4Config.Validate(errs)
+
 	return errs
 }
 
@@ -1019,6 +1018,8 @@ func SetupViper(v *viper.Viper, filename string, bidderInfos BidderInfos) {
 	v.SetDefault("account_defaults.price_floors.max_rules", 100)
 	v.SetDefault("account_defaults.price_floors.max_schema_dims", 3)
 	v.SetDefault("account_defaults.events_enabled", false)
+	v.SetDefault("account_defaults.privacy.ipv6.anon_keep_bits", 56)
+	v.SetDefault("account_defaults.privacy.ipv4.anon_keep_bits", 24)
 
 	v.SetDefault("compression.response.enable_gzip", false)
 	v.SetDefault("compression.request.enable_gzip", false)
diff --git a/endpoints/cookie_sync.go b/endpoints/cookie_sync.go
index 5caa543c..50cb4491 100644
--- a/endpoints/cookie_sync.go
+++ b/endpoints/cookie_sync.go
@@ -149,7 +149,7 @@ func (c *cookieSyncEndpoint) parseRequest(r *http.Request) (usersync.Request, pr
 		}
 	}
 
-	activityControl, activitiesErr := privacy.NewActivityControl(account.Privacy)
+	activityControl, activitiesErr := privacy.NewActivityControl(&account.Privacy)
 	if activitiesErr != nil {
 		if errortypes.ContainsFatalError([]error{activitiesErr}) {
 			activityControl = privacy.ActivityControl{}
diff --git a/endpoints/openrtb2/amp_auction.go b/endpoints/openrtb2/amp_auction.go
index fb4b1928..142f7320 100644
--- a/endpoints/openrtb2/amp_auction.go
+++ b/endpoints/openrtb2/amp_auction.go
@@ -229,7 +229,7 @@ func (deps *endpointDeps) AmpAuction(w http.ResponseWriter, r *http.Request, _ h
 
 	tcf2Config := gdpr.NewTCF2Config(deps.cfg.GDPR.TCF2, account.GDPR)
 
-	activities, activitiesErr := privacy.NewActivityControl(account.Privacy)
+	activities, activitiesErr := privacy.NewActivityControl(&account.Privacy)
 	if activitiesErr != nil {
 		errL = append(errL, activitiesErr)
 		writeError(errL, w, &labels)
diff --git a/endpoints/openrtb2/auction.go b/endpoints/openrtb2/auction.go
index 6ce2c323..6fe5aab9 100644
--- a/endpoints/openrtb2/auction.go
+++ b/endpoints/openrtb2/auction.go
@@ -6,7 +6,6 @@ import (
 	"encoding/json"
 	"errors"
 	"fmt"
-	"github.com/prebid/prebid-server/privacy"
 	"io"
 	"net/http"
 	"net/url"
@@ -15,6 +14,8 @@ import (
 	"strings"
 	"time"
 
+	"github.com/prebid/prebid-server/privacy"
+
 	"github.com/buger/jsonparser"
 	"github.com/gofrs/uuid"
 	"github.com/golang/glog"
@@ -193,7 +194,7 @@ func (deps *endpointDeps) Auction(w http.ResponseWriter, r *http.Request, _ http
 
 	tcf2Config := gdpr.NewTCF2Config(deps.cfg.GDPR.TCF2, account.GDPR)
 
-	activities, activitiesErr := privacy.NewActivityControl(account.Privacy)
+	activities, activitiesErr := privacy.NewActivityControl(&account.Privacy)
 	if activitiesErr != nil {
 		errL = append(errL, activitiesErr)
 		if errortypes.ContainsFatalError(errL) {
@@ -1966,8 +1967,12 @@ func setAuctionTypeImplicitly(r *openrtb_ext.RequestWrapper) {
 }
 
 func setSiteImplicitly(httpReq *http.Request, r *openrtb_ext.RequestWrapper) {
+	if r.Site == nil {
+		r.Site = &openrtb2.Site{}
+	}
+
 	referrerCandidate := httpReq.Referer()
-	if referrerCandidate == "" && r.Site != nil && r.Site.Page != "" {
+	if referrerCandidate == "" && r.Site.Page != "" {
 		referrerCandidate = r.Site.Page // If http referer is disabled and thus has empty value - use site.page instead
 	}
 
@@ -1981,17 +1986,13 @@ func setSiteImplicitly(httpReq *http.Request, r *openrtb_ext.RequestWrapper) {
 		}
 	}
 
-	if r.Site != nil {
-		if siteExt, err := r.GetSiteExt(); err == nil && siteExt.GetAmp() == nil {
-			siteExt.SetAmp(&notAmp)
-		}
+	if siteExt, err := r.GetSiteExt(); err == nil && siteExt.GetAmp() == nil {
+		siteExt.SetAmp(&notAmp)
 	}
+
 }
 
 func setSitePageIfEmpty(site *openrtb2.Site, sitePage string) {
-	if site == nil {
-		site = &openrtb2.Site{}
-	}
 	if site.Page == "" {
 		site.Page = sitePage
 	}
diff --git a/endpoints/openrtb2/sample-requests/invalid-whole/no-site-or-app.json b/endpoints/openrtb2/sample-requests/invalid-whole/no-site-or-app.json
index d21ca4a9..8ec2273e 100644
--- a/endpoints/openrtb2/sample-requests/invalid-whole/no-site-or-app.json
+++ b/endpoints/openrtb2/sample-requests/invalid-whole/no-site-or-app.json
@@ -19,5 +19,5 @@
     ]
   },
   "expectedReturnCode": 400,
-  "expectedErrorMessage": "Invalid request: request.site or request.app must be defined, but not both.\n"
+  "expectedErrorMessage": "Invalid request: request.site should include at least one of request.site.id or request.site.page.\n"
 }
diff --git a/endpoints/openrtb2/video_auction.go b/endpoints/openrtb2/video_auction.go
index 8fc2f844..5f848446 100644
--- a/endpoints/openrtb2/video_auction.go
+++ b/endpoints/openrtb2/video_auction.go
@@ -303,7 +303,7 @@ func (deps *endpointDeps) VideoAuctionEndpoint(w http.ResponseWriter, r *http.Re
 		return
 	}
 
-	activities, activitiesErr := privacy.NewActivityControl(account.Privacy)
+	activities, activitiesErr := privacy.NewActivityControl(&account.Privacy)
 	if activitiesErr != nil {
 		errL = append(errL, activitiesErr)
 		if errortypes.ContainsFatalError(errL) {
diff --git a/endpoints/setuid.go b/endpoints/setuid.go
index 561989e0..30a1cce2 100644
--- a/endpoints/setuid.go
+++ b/endpoints/setuid.go
@@ -104,7 +104,7 @@ func NewSetUIDEndpoint(cfg *config.Configuration, syncersByBidder map[string]use
 			return
 		}
 
-		activities, activitiesErr := privacy.NewActivityControl(account.Privacy)
+		activities, activitiesErr := privacy.NewActivityControl(&account.Privacy)
 		if activitiesErr != nil {
 			if errortypes.ContainsFatalError([]error{activitiesErr}) {
 				activities = privacy.ActivityControl{}
diff --git a/exchange/adapter_builders.go b/exchange/adapter_builders.go
index 84b1f1af..484fe59c 100755
--- a/exchange/adapter_builders.go
+++ b/exchange/adapter_builders.go
@@ -230,6 +230,7 @@ func newAdapterBuilders() map[openrtb_ext.BidderName]adapters.Builder {
 		openrtb_ext.BidderApacdex:           apacdex.Builder,
 		openrtb_ext.BidderApplogy:           applogy.Builder,
 		openrtb_ext.BidderAppnexus:          appnexus.Builder,
+		openrtb_ext.BidderAppstock:          limelightDigital.Builder,
 		openrtb_ext.BidderAppush:            appush.Builder,
 		openrtb_ext.BidderAudienceNetwork:   audienceNetwork.Builder,
 		openrtb_ext.BidderAutomatad:         automatad.Builder,
diff --git a/exchange/utils.go b/exchange/utils.go
index baedcf78..267d928c 100644
--- a/exchange/utils.go
+++ b/exchange/utils.go
@@ -214,7 +214,7 @@ func (rs *requestSplitter) cleanOpenRTBRequests(ctx context.Context,
 
 		privacyEnforcement.TID = !auctionReq.Activities.Allow(privacy.ActivityTransmitTids, scopedName)
 
-		privacyEnforcement.Apply(bidderRequest.BidRequest)
+		privacyEnforcement.Apply(bidderRequest.BidRequest, auctionReq.Account.Privacy)
 		allowedBidderRequests = append(allowedBidderRequests, bidderRequest)
 
 		// GPP downgrade: always downgrade unless we can confirm GPP is supported
diff --git a/floors/enforce.go b/floors/enforce.go
index 1f50dd2b..9318c9d2 100644
--- a/floors/enforce.go
+++ b/floors/enforce.go
@@ -137,7 +137,7 @@ func enforceFloorToBids(bidRequestWrapper *openrtb_ext.RequestWrapper, seatBids
 				}
 
 				bidPrice := rate * bid.Bid.Price
-				if reqImp.BidFloor > bidPrice {
+				if (bidPrice + floorPrecision) < reqImp.BidFloor {
 					rejectedBid := &entities.PbsOrtbSeatBid{
 						Currency: seatBid.Currency,
 						Seat:     seatBid.Seat,
diff --git a/floors/floors.go b/floors/floors.go
index f9e13ec0..52591915 100644
--- a/floors/floors.go
+++ b/floors/floors.go
@@ -16,15 +16,16 @@ type Price struct {
 }
 
 const (
-	defaultCurrency  string = "USD"
-	defaultDelimiter string = "|"
-	catchAll         string = "*"
-	skipRateMin      int    = 0
-	skipRateMax      int    = 100
-	modelWeightMax   int    = 100
-	modelWeightMin   int    = 1
-	enforceRateMin   int    = 0
-	enforceRateMax   int    = 100
+	defaultCurrency  string  = "USD"
+	defaultDelimiter string  = "|"
+	catchAll         string  = "*"
+	skipRateMin      int     = 0
+	skipRateMax      int     = 100
+	modelWeightMax   int     = 100
+	modelWeightMin   int     = 1
+	enforceRateMin   int     = 0
+	enforceRateMax   int     = 100
+	floorPrecision   float64 = 0.01
 )
 
 // EnrichWithPriceFloors checks for floors enabled in account and request and selects floors data from dynamic fetched if present
diff --git a/hooks/hookexecution/executor.go b/hooks/hookexecution/executor.go
index ce516630..5074d4b9 100644
--- a/hooks/hookexecution/executor.go
+++ b/hooks/hookexecution/executor.go
@@ -160,7 +160,7 @@ func (e *hookExecutor) ExecuteProcessedAuctionStage(request *openrtb_ext.Request
 
 	stageName := hooks.StageProcessedAuctionRequest.String()
 	executionCtx := e.newContext(stageName)
-	payload := hookstage.ProcessedAuctionRequestPayload{BidRequest: request.BidRequest}
+	payload := hookstage.ProcessedAuctionRequestPayload{RequestWrapper: request}
 
 	outcome, _, contexts, reject := executeStage(executionCtx, plan, payload, handler, e.metricEngine)
 	outcome.Entity = entityAuctionRequest
diff --git a/hooks/hookstage/processedauctionrequest.go b/hooks/hookstage/processedauctionrequest.go
index 30cb0dab..fe06bc6f 100644
--- a/hooks/hookstage/processedauctionrequest.go
+++ b/hooks/hookstage/processedauctionrequest.go
@@ -3,7 +3,7 @@ package hookstage
 import (
 	"context"
 
-	"github.com/prebid/openrtb/v19/openrtb2"
+	"github.com/prebid/prebid-server/openrtb_ext"
 )
 
 // ProcessedAuctionRequest hooks are invoked after the request is parsed
@@ -23,8 +23,8 @@ type ProcessedAuctionRequest interface {
 	) (HookResult[ProcessedAuctionRequestPayload], error)
 }
 
-// ProcessedAuctionRequestPayload consists of the openrtb2.BidRequest object.
-// Hooks are allowed to modify openrtb2.BidRequest using mutations.
+// ProcessedAuctionRequestPayload consists of the openrtb_ext.RequestWrapper object.
+// Hooks are allowed to modify openrtb_ext.RequestWrapper using mutations.
 type ProcessedAuctionRequestPayload struct {
-	BidRequest *openrtb2.BidRequest
+	RequestWrapper *openrtb_ext.RequestWrapper
 }
diff --git a/openrtb_ext/bid.go b/openrtb_ext/bid.go
index 7dc620d4..85cfed5c 100644
--- a/openrtb_ext/bid.go
+++ b/openrtb_ext/bid.go
@@ -66,6 +66,7 @@ type ExtBidPrebidMeta struct {
 	PrimaryCategoryID    string          `json:"primaryCatId,omitempty"`
 	RendererName         string          `json:"rendererName,omitempty"`
 	RendererVersion      string          `json:"rendererVersion,omitempty"`
+	RendererData         json.RawMessage `json:"rendererData,omitempty"`
 	SecondaryCategoryIDs []string        `json:"secondaryCatIds,omitempty"`
 }
 
diff --git a/openrtb_ext/bidders.go b/openrtb_ext/bidders.go
index c60b9dca..deeb1c58 100644
--- a/openrtb_ext/bidders.go
+++ b/openrtb_ext/bidders.go
@@ -15,7 +15,7 @@ import (
 // BidderName refers to a core bidder id or an alias id.
 type BidderName string
 
-var aliasBidderNames []BidderName
+var aliasBidderToParent map[BidderName]BidderName = map[BidderName]BidderName{}
 
 var coreBidderNames []BidderName = []BidderName{
 	Bidder33Across,
@@ -54,6 +54,7 @@ var coreBidderNames []BidderName = []BidderName{
 	BidderApacdex,
 	BidderApplogy,
 	BidderAppnexus,
+	BidderAppstock,
 	BidderAppush,
 	BidderAudienceNetwork,
 	BidderAutomatad,
@@ -224,17 +225,18 @@ var coreBidderNames []BidderName = []BidderName{
 	BidderZetaGlobalSsp,
 }
 
-func SetAliasBidderName(name BidderName) {
-	aliasBidderNames = append(aliasBidderNames, name)
-	coreBidderNames = append(coreBidderNames, name)
+func GetAliasBidderToParent() map[BidderName]BidderName {
+	return aliasBidderToParent
 }
 
-func GetAliasBidderNamesSet() map[BidderName]struct{} {
-	set := map[BidderName]struct{}{}
-	for _, name := range aliasBidderNames {
-		set[name] = struct{}{}
+func SetAliasBidderName(aliasBidderName string, parentBidderName BidderName) error {
+	if IsBidderNameReserved(aliasBidderName) {
+		return fmt.Errorf("alias %s is a reserved bidder name and cannot be used", aliasBidderName)
 	}
-	return set
+	aliasBidder := BidderName(aliasBidderName)
+	coreBidderNames = append(coreBidderNames, aliasBidder)
+	aliasBidderToParent[aliasBidder] = parentBidderName
+	return nil
 }
 
 func (name BidderName) MarshalJSON() ([]byte, error) {
@@ -344,6 +346,7 @@ const (
 	BidderApacdex           BidderName = "apacdex"
 	BidderApplogy           BidderName = "applogy"
 	BidderAppnexus          BidderName = "appnexus"
+	BidderAppstock          BidderName = "appstock"
 	BidderAppush            BidderName = "appush"
 	BidderAudienceNetwork   BidderName = "audienceNetwork"
 	BidderAutomatad         BidderName = "automatad"
@@ -555,11 +558,11 @@ var bidderNameLookup = func() map[string]BidderName {
 		lookup[bidderNameLower] = name
 	}
 	return lookup
-}()
+}
 
 func NormalizeBidderName(name string) (BidderName, bool) {
 	nameLower := strings.ToLower(name)
-	bidderName, exists := bidderNameLookup[nameLower]
+	bidderName, exists := bidderNameLookup()[nameLower]
 	return bidderName, exists
 }
 
@@ -572,10 +575,42 @@ type BidderParamValidator interface {
 	Schema(name BidderName) string
 }
 
+type bidderParamsFileSystem interface {
+	readDir(name string) ([]os.DirEntry, error)
+	readFile(name string) ([]byte, error)
+	newReferenceLoader(source string) gojsonschema.JSONLoader
+	newSchema(l gojsonschema.JSONLoader) (*gojsonschema.Schema, error)
+	abs(path string) (string, error)
+}
+
+type standardBidderParamsFileSystem struct{}
+
+func (standardBidderParamsFileSystem) readDir(name string) ([]os.DirEntry, error) {
+	return os.ReadDir(name)
+}
+
+func (standardBidderParamsFileSystem) readFile(name string) ([]byte, error) {
+	return os.ReadFile(name)
+}
+
+func (standardBidderParamsFileSystem) newReferenceLoader(source string) gojsonschema.JSONLoader {
+	return gojsonschema.NewReferenceLoader(source)
+}
+
+func (standardBidderParamsFileSystem) newSchema(l gojsonschema.JSONLoader) (*gojsonschema.Schema, error) {
+	return gojsonschema.NewSchema(l)
+}
+
+func (standardBidderParamsFileSystem) abs(path string) (string, error) {
+	return filepath.Abs(path)
+}
+
+var paramsValidator bidderParamsFileSystem = standardBidderParamsFileSystem{}
+
 // NewBidderParamsValidator makes a BidderParamValidator, assuming all the necessary files exist in the filesystem.
 // This will error if, for example, a Bidder gets added but no JSON schema is written for them.
 func NewBidderParamsValidator(schemaDirectory string) (BidderParamValidator, error) {
-	fileInfos, err := os.ReadDir(schemaDirectory)
+	fileInfos, err := paramsValidator.readDir(schemaDirectory)
 	if err != nil {
 		return nil, fmt.Errorf("Failed to read JSON schemas from directory %s. %v", schemaDirectory, err)
 	}
@@ -589,17 +624,17 @@ func NewBidderParamsValidator(schemaDirectory string) (BidderParamValidator, err
 		if _, ok := bidderMap[bidderName]; !ok {
 			return nil, fmt.Errorf("File %s/%s does not match a valid BidderName.", schemaDirectory, fileInfo.Name())
 		}
-		toOpen, err := filepath.Abs(filepath.Join(schemaDirectory, fileInfo.Name()))
+		toOpen, err := paramsValidator.abs(filepath.Join(schemaDirectory, fileInfo.Name()))
 		if err != nil {
 			return nil, fmt.Errorf("Failed to get an absolute representation of the path: %s, %v", toOpen, err)
 		}
-		schemaLoader := gojsonschema.NewReferenceLoader("file:///" + filepath.ToSlash(toOpen))
-		loadedSchema, err := gojsonschema.NewSchema(schemaLoader)
+		schemaLoader := paramsValidator.newReferenceLoader("file:///" + filepath.ToSlash(toOpen))
+		loadedSchema, err := paramsValidator.newSchema(schemaLoader)
 		if err != nil {
 			return nil, fmt.Errorf("Failed to load json schema at %s: %v", toOpen, err)
 		}
 
-		fileBytes, err := os.ReadFile(fmt.Sprintf("%s/%s", schemaDirectory, fileInfo.Name()))
+		fileBytes, err := paramsValidator.readFile(fmt.Sprintf("%s/%s", schemaDirectory, fileInfo.Name()))
 		if err != nil {
 			return nil, fmt.Errorf("Failed to read file %s/%s: %v", schemaDirectory, fileInfo.Name(), err)
 		}
@@ -608,6 +643,15 @@ func NewBidderParamsValidator(schemaDirectory string) (BidderParamValidator, err
 		schemaContents[BidderName(bidderName)] = string(fileBytes)
 	}
 
+	//set alias bidder params schema to its parent
+	for alias, parent := range aliasBidderToParent {
+		parentSchema := schemas[parent]
+		schemas[alias] = parentSchema
+
+		parentSchemaContents := schemaContents[parent]
+		schemaContents[alias] = parentSchemaContents
+	}
+
 	return &bidderParamValidator{
 		schemaContents: schemaContents,
 		parsedSchemas:  schemas,
diff --git a/openrtb_ext/imp_datablocks.go b/openrtb_ext/imp_datablocks.go
index c8659d3f..dc3bae3f 100644
--- a/openrtb_ext/imp_datablocks.go
+++ b/openrtb_ext/imp_datablocks.go
@@ -2,6 +2,5 @@ package openrtb_ext
 
 // ExtImpDatablocks defines the contract for bidrequest.imp[i].ext.prebid.bidder.datablocks
 type ExtImpDatablocks struct {
-	SourceId int    `json:"sourceId"`
-	Host     string `json:"host"`
+	SourceId int `json:"sourceId"`
 }
diff --git a/openrtb_ext/imp_sovrn.go b/openrtb_ext/imp_sovrn.go
index 38d2f785..362463f9 100644
--- a/openrtb_ext/imp_sovrn.go
+++ b/openrtb_ext/imp_sovrn.go
@@ -1,7 +1,8 @@
 package openrtb_ext
 
 type ExtImpSovrn struct {
-	TagId    string  `json:"tagId,omitempty"`
-	Tagid    string  `json:"tagid,omitempty"`
-	BidFloor float64 `json:"bidfloor"`
+	TagId      string  `json:"tagId,omitempty"`
+	Tagid      string  `json:"tagid,omitempty"`
+	BidFloor   float64 `json:"bidfloor,omitempty"`
+	AdUnitCode string  `json:"adunitcode,omitempty"`
 }
diff --git a/openrtb_ext/request.go b/openrtb_ext/request.go
index 6f39f0be..f5418ba4 100644
--- a/openrtb_ext/request.go
+++ b/openrtb_ext/request.go
@@ -352,8 +352,9 @@ type ExtRequestSdk struct {
 }
 
 type ExtRequestSdkRenderer struct {
-	Name    string `json:"name,omitempty"`
-	Version string `json:"version,omitempty"`
+	Name    string          `json:"name,omitempty"`
+	Version string          `json:"version,omitempty"`
+	Data    json.RawMessage `json:"data,omitempty"`
 }
 
 type ExtMultiBid struct {
diff --git a/privacy/activitycontrol.go b/privacy/activitycontrol.go
index fee5c650..5d68ce71 100644
--- a/privacy/activitycontrol.go
+++ b/privacy/activitycontrol.go
@@ -22,7 +22,7 @@ func NewActivityControl(privacyConf *config.AccountPrivacy) (ActivityControl, er
 	ac := ActivityControl{}
 	var err error
 
-	if privacyConf == nil {
+	if privacyConf == nil || privacyConf.AllowActivities == nil {
 		return ac, nil
 	}
 
diff --git a/privacy/enforcement.go b/privacy/enforcement.go
index 1a66cfab..8074d96a 100644
--- a/privacy/enforcement.go
+++ b/privacy/enforcement.go
@@ -1,6 +1,9 @@
 package privacy
 
-import "github.com/prebid/openrtb/v19/openrtb2"
+import (
+	"github.com/prebid/openrtb/v19/openrtb2"
+	"github.com/prebid/prebid-server/config"
+)
 
 // Enforcement represents the privacy policies to enforce for an OpenRTB bid request.
 type Enforcement struct {
@@ -27,8 +30,8 @@ func (e Enforcement) AnyActivities() bool {
 }
 
 // Apply cleans personally identifiable information from an OpenRTB bid request.
-func (e Enforcement) Apply(bidRequest *openrtb2.BidRequest) {
-	e.apply(bidRequest, NewScrubber())
+func (e Enforcement) Apply(bidRequest *openrtb2.BidRequest, privacy config.AccountPrivacy) {
+	e.apply(bidRequest, NewScrubber(privacy.IPv6Config, privacy.IPv4Config))
 }
 
 func (e Enforcement) apply(bidRequest *openrtb2.BidRequest, scrubber Scrubber) {
@@ -55,21 +58,16 @@ func (e Enforcement) getDeviceIDScrubStrategy() ScrubStrategyDeviceID {
 
 func (e Enforcement) getIPv4ScrubStrategy() ScrubStrategyIPV4 {
 	if e.COPPA || e.GDPRGeo || e.CCPA || e.LMT {
-		return ScrubStrategyIPV4Lowest8
+		return ScrubStrategyIPV4Subnet
 	}
 
 	return ScrubStrategyIPV4None
 }
 
 func (e Enforcement) getIPv6ScrubStrategy() ScrubStrategyIPV6 {
-	if e.COPPA {
-		return ScrubStrategyIPV6Lowest32
+	if e.GDPRGeo || e.CCPA || e.LMT || e.COPPA {
+		return ScrubStrategyIPV6Subnet
 	}
-
-	if e.GDPRGeo || e.CCPA || e.LMT {
-		return ScrubStrategyIPV6Lowest16
-	}
-
 	return ScrubStrategyIPV6None
 }
 
diff --git a/privacy/scrubber.go b/privacy/scrubber.go
index f2fb790d..4cc90c82 100644
--- a/privacy/scrubber.go
+++ b/privacy/scrubber.go
@@ -2,8 +2,10 @@ package privacy
 
 import (
 	"encoding/json"
+	"github.com/prebid/prebid-server/config"
+	"github.com/prebid/prebid-server/util/iputil"
 	"github.com/prebid/prebid-server/util/ptrutil"
-	"strings"
+	"net"
 
 	"github.com/prebid/openrtb/v19/openrtb2"
 )
@@ -15,8 +17,8 @@ const (
 	// ScrubStrategyIPV4None does not remove any part of an IPV4 address.
 	ScrubStrategyIPV4None ScrubStrategyIPV4 = iota
 
-	// ScrubStrategyIPV4Lowest8 zeroes out the last 8 bits of an IPV4 address.
-	ScrubStrategyIPV4Lowest8
+	// ScrubStrategyIPV4Subnet zeroes out the last 8 bits of an IPV4 address.
+	ScrubStrategyIPV4Subnet
 )
 
 // ScrubStrategyIPV6 defines the approach to scrub PII from an IPV6 address.
@@ -26,11 +28,8 @@ const (
 	// ScrubStrategyIPV6None does not remove any part of an IPV6 address.
 	ScrubStrategyIPV6None ScrubStrategyIPV6 = iota
 
-	// ScrubStrategyIPV6Lowest16 zeroes out the last 16 bits of an IPV6 address.
-	ScrubStrategyIPV6Lowest16
-
-	// ScrubStrategyIPV6Lowest32 zeroes out the last 32 bits of an IPV6 address.
-	ScrubStrategyIPV6Lowest32
+	// ScrubStrategyIPV6Subnet zeroes out the last 16 bits of an IPV6 sub net address.
+	ScrubStrategyIPV6Subnet
 )
 
 // ScrubStrategyGeo defines the approach to scrub PII from geographical data.
@@ -76,14 +75,20 @@ type Scrubber interface {
 	ScrubUser(user *openrtb2.User, strategy ScrubStrategyUser, geo ScrubStrategyGeo) *openrtb2.User
 }
 
-type scrubber struct{}
+type scrubber struct {
+	ipV6 config.IPv6
+	ipV4 config.IPv4
+}
 
 // NewScrubber returns an OpenRTB scrubber.
-func NewScrubber() Scrubber {
-	return scrubber{}
+func NewScrubber(ipV6 config.IPv6, ipV4 config.IPv4) Scrubber {
+	return scrubber{
+		ipV6: ipV6,
+		ipV4: ipV4,
+	}
 }
 
-func (scrubber) ScrubRequest(bidRequest *openrtb2.BidRequest, enforcement Enforcement) *openrtb2.BidRequest {
+func (s scrubber) ScrubRequest(bidRequest *openrtb2.BidRequest, enforcement Enforcement) *openrtb2.BidRequest {
 	var userExtParsed map[string]json.RawMessage
 	userExtModified := false
 
@@ -169,8 +174,8 @@ func (scrubber) ScrubRequest(bidRequest *openrtb2.BidRequest, enforcement Enforc
 			if deviceCopy.Geo != nil {
 				deviceCopy.Geo = scrubGeoPrecision(deviceCopy.Geo)
 			}
-			deviceCopy.IP = scrubIPV4Lowest8(deviceCopy.IP)
-			deviceCopy.IPv6 = scrubIPV6Lowest32Bits(deviceCopy.IPv6)
+			deviceCopy.IP = scrubIP(deviceCopy.IP, s.ipV4.AnonKeepBits, iputil.IPv4BitSize)
+			deviceCopy.IPv6 = scrubIP(deviceCopy.IPv6, s.ipV6.AnonKeepBits, iputil.IPv6BitSize)
 		}
 	}
 
@@ -179,7 +184,7 @@ func (scrubber) ScrubRequest(bidRequest *openrtb2.BidRequest, enforcement Enforc
 	return bidRequest
 }
 
-func (scrubber) ScrubDevice(device *openrtb2.Device, id ScrubStrategyDeviceID, ipv4 ScrubStrategyIPV4, ipv6 ScrubStrategyIPV6, geo ScrubStrategyGeo) *openrtb2.Device {
+func (s scrubber) ScrubDevice(device *openrtb2.Device, id ScrubStrategyDeviceID, ipv4 ScrubStrategyIPV4, ipv6 ScrubStrategyIPV6, geo ScrubStrategyGeo) *openrtb2.Device {
 	if device == nil {
 		return nil
 	}
@@ -198,15 +203,13 @@ func (scrubber) ScrubDevice(device *openrtb2.Device, id ScrubStrategyDeviceID, i
 	}
 
 	switch ipv4 {
-	case ScrubStrategyIPV4Lowest8:
-		deviceCopy.IP = scrubIPV4Lowest8(device.IP)
+	case ScrubStrategyIPV4Subnet:
+		deviceCopy.IP = scrubIP(device.IP, s.ipV4.AnonKeepBits, iputil.IPv4BitSize)
 	}
 
 	switch ipv6 {
-	case ScrubStrategyIPV6Lowest16:
-		deviceCopy.IPv6 = scrubIPV6Lowest16Bits(device.IPv6)
-	case ScrubStrategyIPV6Lowest32:
-		deviceCopy.IPv6 = scrubIPV6Lowest32Bits(device.IPv6)
+	case ScrubStrategyIPV6Subnet:
+		deviceCopy.IPv6 = scrubIP(device.IPv6, s.ipV6.AnonKeepBits, iputil.IPv6BitSize)
 	}
 
 	switch geo {
@@ -244,44 +247,13 @@ func (scrubber) ScrubUser(user *openrtb2.User, strategy ScrubStrategyUser, geo S
 	return &userCopy
 }
 
-func scrubIPV4Lowest8(ip string) string {
-	i := strings.LastIndex(ip, ".")
-	if i == -1 {
-		return ""
-	}
-
-	return ip[0:i] + ".0"
-}
-
-func scrubIPV6Lowest16Bits(ip string) string {
-	ip = removeLowestIPV6Segment(ip)
-
-	if ip != "" {
-		ip += ":0"
-	}
-
-	return ip
-}
-
-func scrubIPV6Lowest32Bits(ip string) string {
-	ip = removeLowestIPV6Segment(ip)
-	ip = removeLowestIPV6Segment(ip)
-
-	if ip != "" {
-		ip += ":0:0"
-	}
-
-	return ip
-}
-
-func removeLowestIPV6Segment(ip string) string {
-	i := strings.LastIndex(ip, ":")
-
-	if i == -1 {
+func scrubIP(ip string, ones, bits int) string {
+	if ip == "" {
 		return ""
 	}
-
-	return ip[0:i]
+	ipMask := net.CIDRMask(ones, bits)
+	ipMasked := net.ParseIP(ip).Mask(ipMask)
+	return ipMasked.String()
 }
 
 func scrubGeoFull(geo *openrtb2.Geo) *openrtb2.Geo {
diff --git a/router/router.go b/router/router.go
index fcf674b4..f7270445 100644
--- a/router/router.go
+++ b/router/router.go
@@ -55,6 +55,10 @@ import (
 // This function stores the file contents in memory, and should not be used on large directories.
 // If the root directory, or any of the files in it, cannot be read, then the program will exit.
 func NewJsonDirectoryServer(schemaDirectory string, validator openrtb_ext.BidderParamValidator, aliases map[string]string) httprouter.Handle {
+	return newJsonDirectoryServer(schemaDirectory, validator, aliases, openrtb_ext.GetAliasBidderToParent())
+}
+
+func newJsonDirectoryServer(schemaDirectory string, validator openrtb_ext.BidderParamValidator, aliases map[string]string, yamlAliases map[openrtb_ext.BidderName]openrtb_ext.BidderName) httprouter.Handle {
 	// Slurp the files into memory first, since they're small and it minimizes request latency.
 	files, err := os.ReadDir(schemaDirectory)
 	if err != nil {
@@ -73,6 +77,11 @@ func NewJsonDirectoryServer(schemaDirectory string, validator openrtb_ext.Bidder
 		data[bidder] = json.RawMessage(validator.Schema(bidderName))
 	}
 
+	// Add in any aliases
+	for aliasName, parentBidder := range yamlAliases {
+		data[string(aliasName)] = json.RawMessage(validator.Schema(parentBidder))
+	}
+
 	// Add in any default aliases
 	for aliasName, bidderName := range aliases {
 		bidderData, ok := data[bidderName]
diff --git a/static/bidder-info/appstock.yaml b/static/bidder-info/appstock.yaml
new file mode 100644
index 00000000..28f420e9
--- /dev/null
+++ b/static/bidder-info/appstock.yaml
@@ -0,0 +1,16 @@
+endpoint: "http://ads-pbs.pre.vr-tb.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-info/datablocks.yaml b/static/bidder-info/datablocks.yaml
index 418b0cab..1f8d6e8d 100644
--- a/static/bidder-info/datablocks.yaml
+++ b/static/bidder-info/datablocks.yaml
@@ -1,4 +1,4 @@
-endpoint: "http://{{.Host}}/openrtb2?sid={{.SourceId}}"
+endpoint: "http://pbserver.dblks.net/openrtb2?sid={{.SourceId}}"
 maintainer:
   email: "prebid@datablocks.net"
 capabilities:
diff --git a/static/bidder-info/huaweiads.yaml b/static/bidder-info/huaweiads.yaml
index 8fe35b4e..fc36ff95 100644
--- a/static/bidder-info/huaweiads.yaml
+++ b/static/bidder-info/huaweiads.yaml
@@ -1,7 +1,7 @@
 endpoint: "https://acd.op.hicloud.com/ppsadx/getResult"
 disabled: true
 maintainer:
-  email: hwads@huawei.com
+  email: prebid@huawei.com
 gvlVendorID: 856
 modifyingVastXmlAllowed: true
 capabilities:
diff --git a/static/bidder-info/sovrn.yaml b/static/bidder-info/sovrn.yaml
index 06dadcf9..62d74152 100644
--- a/static/bidder-info/sovrn.yaml
+++ b/static/bidder-info/sovrn.yaml
@@ -17,6 +17,5 @@ userSync:
     url: "https://ap.lijit.com/pixel?redir={{.RedirectURL}}"
     userMacro: "$UID"
   iframe:
-    url: "https://ap.lijit.com/beacon/prebid-server/?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&url={{.RedirectURL}}"
+    url: "https://ap.lijit.com/beacon/prebid-server/?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&gpp={{.GPP}}&gpp_sid={{.GPPSID}}&url={{.RedirectURL}}"
     userMacro: "$UID"
-    
\ No newline at end of file
diff --git a/static/bidder-info/taboola.yaml b/static/bidder-info/taboola.yaml
index 348165a9..436f7469 100644
--- a/static/bidder-info/taboola.yaml
+++ b/static/bidder-info/taboola.yaml
@@ -1,4 +1,4 @@
-endpoint: "https://{{.MediaType}}.bidder.taboola.com/OpenRTB/PS/auction/{{.GvlID}}/{{.PublisherID}}"
+endpoint: "https://{{.MediaType}}.bidder.taboola.com/OpenRTB/PS/auction?exchange={{.GvlID}}&publisher={{.PublisherID}}"
 maintainer:
   email:  ps-team@taboola.com
 gvlVendorID: 42
diff --git a/static/bidder-params/appstock.json b/static/bidder-params/appstock.json
new file mode 100644
index 00000000..eb2251e4
--- /dev/null
+++ b/static/bidder-params/appstock.json
@@ -0,0 +1,23 @@
+{
+  "$schema": "http://json-schema.org/draft-04/schema#",
+  "title": "Appstock Adapter Params",
+  "description": "A schema which validates params accepted by the Appstock 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/static/bidder-params/datablocks.json b/static/bidder-params/datablocks.json
index 2abb9867..59b7f947 100644
--- a/static/bidder-params/datablocks.json
+++ b/static/bidder-params/datablocks.json
@@ -9,11 +9,7 @@
         "type": "integer",
         "minimum": 1,
         "description": "Website Source Id"
-      },
-      "host": {
-        "type": "string",
-        "description": "Network Host to request from"
       }
   },
-  "required": ["host", "sourceId"]
+  "required": ["sourceId"]
 }
diff --git a/static/bidder-params/sovrn.json b/static/bidder-params/sovrn.json
index 6b345b55..d821a808 100644
--- a/static/bidder-params/sovrn.json
+++ b/static/bidder-params/sovrn.json
@@ -16,6 +16,10 @@
     "bidfloor": {
       "type": "number",
       "description": "The minimium acceptable bid, in CPM, using US Dollars"
+    },
+    "adunitcode": {
+      "type": "string",
+      "description": "The string which identifies Ad Unit"
     }
   },
   "oneOf": [
diff --git a/util/iputil/parse.go b/util/iputil/parse.go
index bcb00760..c226ece8 100644
--- a/util/iputil/parse.go
+++ b/util/iputil/parse.go
@@ -15,6 +15,14 @@ const (
 	IPv6       IPVersion = 6
 )
 
+const (
+	IPv4BitSize = 32
+	IPv6BitSize = 128
+
+	IPv4DefaultMaskingBitSize = 24
+	IPv6DefaultMaskingBitSize = 56
+)
+
 // ParseIP parses v as an ip address returning the result and version, or nil and unknown if invalid.
 func ParseIP(v string) (net.IP, IPVersion) {
 	if ip := net.ParseIP(v); ip != nil {


Test patch:
diff --git a/account/account_test.go b/account/account_test.go
index 4bac0894..d2694abc 100644
--- a/account/account_test.go
+++ b/account/account_test.go
@@ -12,17 +12,19 @@ import (
 	"github.com/prebid/prebid-server/metrics"
 	"github.com/prebid/prebid-server/openrtb_ext"
 	"github.com/prebid/prebid-server/stored_requests"
+	"github.com/prebid/prebid-server/util/iputil"
 	"github.com/prebid/prebid-server/util/ptrutil"
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/mock"
 )
 
 var mockAccountData = map[string]json.RawMessage{
-	"valid_acct":                json.RawMessage(`{"disabled":false}`),
-	"disabled_acct":             json.RawMessage(`{"disabled":true}`),
-	"malformed_acct":            json.RawMessage(`{"disabled":"invalid type"}`),
-	"gdpr_channel_enabled_acct": json.RawMessage(`{"disabled":false,"gdpr":{"channel_enabled":{"amp":true}}}`),
-	"ccpa_channel_enabled_acct": json.RawMessage(`{"disabled":false,"ccpa":{"channel_enabled":{"amp":true}}}`),
+	"valid_acct":                                   json.RawMessage(`{"disabled":false}`),
+	"invalid_acct_ipv6_ipv4":                       json.RawMessage(`{"disabled":false, "privacy": {"ipv6": {"anon_keep_bits": -32}, "ipv4": {"anon_keep_bits": -16}}}`),
+	"disabled_acct":                                json.RawMessage(`{"disabled":true}`),
+	"malformed_acct":                               json.RawMessage(`{"disabled":"invalid type"}`),
+	"gdpr_channel_enabled_acct":                    json.RawMessage(`{"disabled":false,"gdpr":{"channel_enabled":{"amp":true}}}`),
+	"ccpa_channel_enabled_acct":                    json.RawMessage(`{"disabled":false,"ccpa":{"channel_enabled":{"amp":true}}}`),
 	"gdpr_channel_enabled_deprecated_purpose_acct": json.RawMessage(`{"disabled":false,"gdpr":{"purpose1":{"enforce_purpose":"full"}, "channel_enabled":{"amp":true}}}`),
 	"gdpr_deprecated_purpose1":                     json.RawMessage(`{"disabled":false,"gdpr":{"purpose1":{"enforce_purpose":"full"}}}`),
 	"gdpr_deprecated_purpose2":                     json.RawMessage(`{"disabled":false,"gdpr":{"purpose2":{"enforce_purpose":"full"}}}`),
@@ -54,6 +56,8 @@ func TestGetAccount(t *testing.T) {
 		required bool
 		// account_defaults.disabled
 		disabled bool
+		// checkDefaultIP indicates IPv6 and IPv6 should be set to default values
+		checkDefaultIP bool
 		// expected error, or nil if account should be found
 		err error
 	}{
@@ -78,6 +82,8 @@ func TestGetAccount(t *testing.T) {
 		{accountID: "valid_acct", required: false, disabled: true, err: nil},
 		{accountID: "valid_acct", required: true, disabled: true, err: nil},
 
+		{accountID: "invalid_acct_ipv6_ipv4", required: true, disabled: false, err: nil, checkDefaultIP: true},
+
 		// pubID given and matches a host account explicitly disabled (Disabled: true on account json)
 		{accountID: "disabled_acct", required: false, disabled: false, err: &errortypes.BlacklistedAcct{}},
 		{accountID: "disabled_acct", required: true, disabled: false, err: &errortypes.BlacklistedAcct{}},
@@ -129,6 +135,10 @@ func TestGetAccount(t *testing.T) {
 				assert.Nil(t, account, "return account must be nil on error")
 				assert.IsType(t, test.err, errors[0], "error is of unexpected type")
 			}
+			if test.checkDefaultIP {
+				assert.Equal(t, account.Privacy.IPv6Config.AnonKeepBits, iputil.IPv6DefaultMaskingBitSize, "ipv6 should be set to default value")
+				assert.Equal(t, account.Privacy.IPv4Config.AnonKeepBits, iputil.IPv4DefaultMaskingBitSize, "ipv4 should be set to default value")
+			}
 		})
 	}
 }
diff --git a/adapters/datablocks/datablocks_test.go b/adapters/datablocks/datablocks_test.go
index 9a5fb9a2..3d4d746d 100644
--- a/adapters/datablocks/datablocks_test.go
+++ b/adapters/datablocks/datablocks_test.go
@@ -11,7 +11,7 @@ import (
 
 func TestJsonSamples(t *testing.T) {
 	bidder, buildErr := Builder(openrtb_ext.BidderDatablocks, config.Adapter{
-		Endpoint: "http://{{.Host}}/openrtb2?sid={{.SourceId}}"}, config.Server{ExternalUrl: "http://hosturl.com", GvlID: 1, DataCenter: "2"})
+		Endpoint: "http://pbserver.dblks.net/openrtb2?sid={{.SourceId}}"}, config.Server{ExternalUrl: "http://hosturl.com", GvlID: 1, DataCenter: "2"})
 
 	if buildErr != nil {
 		t.Fatalf("Builder returned unexpected error %v", buildErr)
diff --git a/adapters/datablocks/datablockstest/exemplary/multi-request.json b/adapters/datablocks/datablockstest/exemplary/multi-request.json
index 286e058a..eafd8092 100644
--- a/adapters/datablocks/datablockstest/exemplary/multi-request.json
+++ b/adapters/datablocks/datablockstest/exemplary/multi-request.json
@@ -17,7 +17,7 @@
             {
                 "bidder":
                 {
-                    "host": "search.nutella.datablocks.net",
+                    "host": "pbserver.dblks.net",
                     "sourceId": 906295
                 }
             }
@@ -34,7 +34,7 @@
             {
                 "bidder":
                 {
-                    "host": "search.nutella.datablocks.net",
+                    "host": "pbserver.dblks.net",
                     "sourceId": 906295
                 }
             }
@@ -54,7 +54,7 @@
     {
         "expectedRequest":
         {
-            "uri": "http://search.nutella.datablocks.net/openrtb2?sid=906295",
+            "uri": "http://pbserver.dblks.net/openrtb2?sid=906295",
             "body":
             {
                 "id": "some-request-id",
@@ -73,7 +73,7 @@
                     {
                         "bidder":
                         {
-                            "host": "search.nutella.datablocks.net",
+                            "host": "pbserver.dblks.net",
                             "sourceId": 906295
                         }
                     }
@@ -91,7 +91,7 @@
                     {
                         "bidder":
                         {
-                            "host": "search.nutella.datablocks.net",
+                            "host": "pbserver.dblks.net",
                             "sourceId": 906295
                         }
                     }
diff --git a/adapters/datablocks/datablockstest/exemplary/native.json b/adapters/datablocks/datablockstest/exemplary/native.json
index 9487079c..c4cf189e 100644
--- a/adapters/datablocks/datablockstest/exemplary/native.json
+++ b/adapters/datablocks/datablockstest/exemplary/native.json
@@ -14,7 +14,7 @@
             {
                 "bidder":
                 {
-                    "host": "search.nutella.datablocks.net",
+                    "host": "pbserver.dblks.net",
                     "sourceId": 906295
                 }
             }
@@ -38,7 +38,7 @@
     {
         "expectedRequest":
         {
-            "uri": "http://search.nutella.datablocks.net/openrtb2?sid=906295",
+            "uri": "http://pbserver.dblks.net/openrtb2?sid=906295",
             "body":
             {
                 "id": "some-request-id",
@@ -54,7 +54,7 @@
                     {
                         "bidder":
                         {
-                            "host": "search.nutella.datablocks.net",
+                            "host": "pbserver.dblks.net",
                             "sourceId": 906295
                         }
                     }
diff --git a/adapters/datablocks/datablockstest/exemplary/simple-banner.json b/adapters/datablocks/datablockstest/exemplary/simple-banner.json
index 289549ab..5c01a29a 100644
--- a/adapters/datablocks/datablockstest/exemplary/simple-banner.json
+++ b/adapters/datablocks/datablockstest/exemplary/simple-banner.json
@@ -21,7 +21,7 @@
             {
                 "bidder":
                 {
-                    "host": "search.nutella.datablocks.net",
+                    "host": "pbserver.dblks.net",
                     "sourceId": 906295
                 }
             }
@@ -41,7 +41,7 @@
     {
         "expectedRequest":
         {
-            "uri": "http://search.nutella.datablocks.net/openrtb2?sid=906295",
+            "uri": "http://pbserver.dblks.net/openrtb2?sid=906295",
             "body":
             {
                 "id": "some-request-id",
@@ -64,7 +64,7 @@
                     {
                         "bidder":
                         {
-                            "host": "search.nutella.datablocks.net",
+                            "host": "pbserver.dblks.net",
                             "sourceId": 906295
                         }
                     }
diff --git a/adapters/datablocks/datablockstest/exemplary/simple-video.json b/adapters/datablocks/datablockstest/exemplary/simple-video.json
index 6f807310..fcb2defa 100644
--- a/adapters/datablocks/datablockstest/exemplary/simple-video.json
+++ b/adapters/datablocks/datablockstest/exemplary/simple-video.json
@@ -18,7 +18,7 @@
             {
                 "bidder":
                 {
-                    "host": "search.nutella.datablocks.net",
+                    "host": "pbserver.dblks.net",
                     "sourceId": 906295
                 }
             }
@@ -39,7 +39,7 @@
     {
         "expectedRequest":
         {
-            "uri": "http://search.nutella.datablocks.net/openrtb2?sid=906295",
+            "uri": "http://pbserver.dblks.net/openrtb2?sid=906295",
             "body":
             {
                 "id": "some-request-id",
@@ -59,7 +59,7 @@
                     {
                         "bidder":
                         {
-                            "host": "search.nutella.datablocks.net",
+                            "host": "pbserver.dblks.net",
                             "sourceId": 906295
                         }
                     }
diff --git a/adapters/datablocks/datablockstest/supplemental/bad-host.json b/adapters/datablocks/datablockstest/supplemental/bad-host.json
deleted file mode 100644
index cee5efbe..00000000
--- a/adapters/datablocks/datablockstest/supplemental/bad-host.json
+++ /dev/null
@@ -1,33 +0,0 @@
-{
-  "mockBidRequest": {
-    "id": "bad-host-test",
-    "imp": [
-      {
-        "id": "bad-host-test-imp",
-        "banner": {
-          "format": [
-            {
-              "w": 300,
-              "h": 250
-            }
-          ]
-        },
-        "ext": 
-        {
-          "bidder":
-          {
-              "host": "",
-              "sourceId": 123
-          }
-        }
-      }
-    ]
-  },
-
-  "expectedMakeRequestsErrors": [
-    {
-      "value": "Invalid/Missing Host",
-      "comparison": "literal"
-    } 
-  ]
-}
diff --git a/adapters/datablocks/datablockstest/supplemental/bad-response-body.json b/adapters/datablocks/datablockstest/supplemental/bad-response-body.json
index 7c4801e1..e60c6aab 100644
--- a/adapters/datablocks/datablockstest/supplemental/bad-response-body.json
+++ b/adapters/datablocks/datablockstest/supplemental/bad-response-body.json
@@ -17,7 +17,7 @@
             {
                 "bidder":
                 {
-                    "host": "search.nutella.datablocks.net",
+                    "host": "pbserver.dblks.net",
                     "sourceId": 123
                 }
             }
@@ -37,7 +37,7 @@
     {
         "expectedRequest":
         {
-            "uri": "http://search.nutella.datablocks.net/openrtb2?sid=123",
+            "uri": "http://pbserver.dblks.net/openrtb2?sid=123",
             "body":
             {
                 "id": "some-request-id",
@@ -56,7 +56,7 @@
                     {
                         "bidder":
                         {
-                            "host": "search.nutella.datablocks.net",
+                            "host": "pbserver.dblks.net",
                             "sourceId": 123
                         }
                     }
diff --git a/adapters/datablocks/datablockstest/supplemental/bad-server-response.json b/adapters/datablocks/datablockstest/supplemental/bad-server-response.json
index da2924f6..cdcc0c37 100644
--- a/adapters/datablocks/datablockstest/supplemental/bad-server-response.json
+++ b/adapters/datablocks/datablockstest/supplemental/bad-server-response.json
@@ -17,7 +17,7 @@
             {
                 "bidder":
                 {
-                    "host": "search.nutella.datablocks.net",
+                    "host": "pbserver.dblks.net",
                     "sourceId": 123
                 }
             }
@@ -37,7 +37,7 @@
     {
         "expectedRequest":
         {
-            "uri": "http://search.nutella.datablocks.net/openrtb2?sid=123",
+            "uri": "http://pbserver.dblks.net/openrtb2?sid=123",
             "body":
             {
                 "id": "some-request-id",
@@ -56,7 +56,7 @@
                     {
                         "bidder":
                         {
-                            "host": "search.nutella.datablocks.net",
+                            "host": "pbserver.dblks.net",
                             "sourceId": 123
                         }
                     }
diff --git a/adapters/datablocks/datablockstest/supplemental/bad-sourceId.json b/adapters/datablocks/datablockstest/supplemental/bad-sourceId.json
index 08f2fddf..3182e71d 100644
--- a/adapters/datablocks/datablockstest/supplemental/bad-sourceId.json
+++ b/adapters/datablocks/datablockstest/supplemental/bad-sourceId.json
@@ -16,7 +16,7 @@
         {
           "bidder":
           {
-              "host": "search.nutella.datablocks.net",
+              "host": "pbserver.dblks.net",
               "sourceId": 0
           }
         }
diff --git a/adapters/datablocks/datablockstest/supplemental/missing-extparam.json b/adapters/datablocks/datablockstest/supplemental/missing-extparam.json
deleted file mode 100644
index d272cd53..00000000
--- a/adapters/datablocks/datablockstest/supplemental/missing-extparam.json
+++ /dev/null
@@ -1,30 +0,0 @@
-{
-  "mockBidRequest": {
-    "id": "missing-extbid-test",
-    "imp": [
-      {
-        "id": "missing-extbid-test",
-        "banner": {
-          "format": [
-            {
-              "w": 300,
-              "h": 250
-            }
-          ]
-        },
-        "ext": {
-          "sourceId":54326
-        }
-      }
-    ]
-  },
-
-  "expectedMakeRequestsErrors": [
-    {
-      "value": "Cannot Resolve host or sourceId: unexpected end of JSON input",
-      "comparison": "literal"
-    } 
-  ]
-
-  
-}
diff --git a/adapters/datablocks/datablockstest/supplemental/no-content-response.json b/adapters/datablocks/datablockstest/supplemental/no-content-response.json
index 7fc1b75c..0e53f472 100644
--- a/adapters/datablocks/datablockstest/supplemental/no-content-response.json
+++ b/adapters/datablocks/datablockstest/supplemental/no-content-response.json
@@ -17,7 +17,7 @@
             {
                 "bidder":
                 {
-                    "host": "search.nutella.datablocks.net",
+                    "host": "pbserver.dblks.net",
                     "sourceId": 123
                 }
             }
@@ -37,7 +37,7 @@
     {
         "expectedRequest":
         {
-            "uri": "http://search.nutella.datablocks.net/openrtb2?sid=123",
+            "uri": "http://pbserver.dblks.net/openrtb2?sid=123",
             "body":
             {
                 "id": "some-request-id",
@@ -56,7 +56,7 @@
                     {
                         "bidder":
                         {
-                            "host": "search.nutella.datablocks.net",
+                            "host": "pbserver.dblks.net",
                             "sourceId": 123
                         }
                     }
diff --git a/adapters/huaweiads/huaweiadstest/exemplary/nativeIncludeVideo.json b/adapters/huaweiads/huaweiadstest/exemplary/nativeIncludeVideo.json
index 8a7ca1b0..24aad066 100644
--- a/adapters/huaweiads/huaweiadstest/exemplary/nativeIncludeVideo.json
+++ b/adapters/huaweiads/huaweiadstest/exemplary/nativeIncludeVideo.json
@@ -95,9 +95,7 @@
               "detailedCreativeTypeList": [
                 "903"
               ],
-              "h": 200,
-              "test": 1,
-              "w": 200
+              "test": 1
             }
           ],
           "device": {
diff --git a/adapters/huaweiads/huaweiadstest/exemplary/nativeSingleImage.json b/adapters/huaweiads/huaweiadstest/exemplary/nativeSingleImage.json
index 240098e0..8e86675b 100644
--- a/adapters/huaweiads/huaweiadstest/exemplary/nativeSingleImage.json
+++ b/adapters/huaweiads/huaweiadstest/exemplary/nativeSingleImage.json
@@ -5,7 +5,7 @@
       {
         "id": "test-imp-id",
         "native": {
-          "request": "{\"context\":2,\"contextsubtype\":20,\"plcmttype\":1,\"plcmtcnt\":1,\"seq\":0,\"aurlsupport\":0,\"durlsupport\":0,\"eventtrackers\":[{\"event\":1,\"methods\":[1,2]}],\"privacy\":0,\"assets\":[{\"id\":100,\"title\":{\"len\":90},\"required\":1},{\"id\":103,\"img\":{\"type\":3,\"wmin\":200,\"hmin\":200},\"required\":1},{\"id\":105,\"data\":{\"type\":2,\"len\":90},\"required\":1}],\"ver\":\"1.2\"}",
+          "request": "{\"context\":2,\"contextsubtype\":20,\"plcmttype\":1,\"plcmtcnt\":1,\"seq\":0,\"aurlsupport\":0,\"durlsupport\":0,\"eventtrackers\":[{\"event\":1,\"methods\":[1,2]}],\"privacy\":0,\"assets\":[{\"id\":101,\"title\":{\"len\":90},\"required\":1},{\"id\":102,\"img\":{\"type\":3,\"wmin\":200,\"hmin\":200},\"required\":1},{\"id\":103,\"data\":{\"type\":2,\"len\":90},\"required\":1},{\"id\":105,\"data\":{\"type\":12,\"len\":90}}],\"ver\":\"1.2\"}",
           "ver": "1.2"
         },
         "ext": {
@@ -93,9 +93,7 @@
               "detailedCreativeTypeList": [
                 "901"
               ],
-              "h": 200,
-              "test": 1,
-              "w": 200
+              "test": 1
             }
           ],
           "device": {
@@ -166,6 +164,7 @@
                     "appId": "101219405",
                     "appPromotionChannel": "401721412",
                     "clickUrl": "https://ads.huawei.com/usermgtportal/home/index.html#/",
+                    "cta": "install",
                     "duration": 6038,
                     "description": "",
                     "icon": [
@@ -300,7 +299,7 @@
               "huaweiads"
             ],
             "crid": "58022259",
-            "adm": "{\"ver\":\"1.2\",\"assets\":[{\"id\":100,\"title\":{\"text\":\"/test/\",\"len\":6}},{\"id\":103,\"img\":{\"type\":3,\"url\":\"http://image.jpg\",\"w\":720,\"h\":1280}},{\"id\":105,\"data\":{\"label\":\"desc\",\"value\":\"\"}}],\"link\":{\"url\":\"https://ads.huawei.com/usermgtportal/home/index.html#/\",\"clicktrackers\":[\"http://test/click\"]},\"eventtrackers\":[{\"event\":1,\"method\":1,\"url\":\"http://test/imp\"}]}",
+            "adm": "{\"ver\":\"1.2\",\"assets\":[{\"id\":101,\"title\":{\"text\":\"/test/\",\"len\":6}},{\"id\":102,\"img\":{\"type\":3,\"url\":\"http://image.jpg\",\"w\":720,\"h\":1280}},{\"id\":103,\"data\":{\"label\":\"desc\",\"value\":\"\"}},{\"id\":105,\"data\":{\"type\":12,\"value\":\"install\"}}],\"link\":{\"url\":\"https://ads.huawei.com/usermgtportal/home/index.html#/\",\"clicktrackers\":[\"http://test/click\"]},\"eventtrackers\":[{\"event\":1,\"method\":1,\"url\":\"http://test/imp\"}]}",
             "id": "test-imp-id",
             "impid": "test-imp-id",
             "price": 2.8,
diff --git a/adapters/huaweiads/huaweiadstest/exemplary/nativeThreeImage.json b/adapters/huaweiads/huaweiadstest/exemplary/nativeThreeImage.json
index 9b46072d..c29b2352 100644
--- a/adapters/huaweiads/huaweiadstest/exemplary/nativeThreeImage.json
+++ b/adapters/huaweiads/huaweiadstest/exemplary/nativeThreeImage.json
@@ -92,9 +92,7 @@
               "detailedCreativeTypeList": [
                 "904"
               ],
-              "h": 200,
-              "test": 1,
-              "w": 200
+              "test": 1
             }
           ],
           "device": {
diff --git a/adapters/huaweiads/huaweiadstest/exemplary/nativeThreeImageIncludeIcon.json b/adapters/huaweiads/huaweiadstest/exemplary/nativeThreeImageIncludeIcon.json
index f78f50e2..5d3af506 100644
--- a/adapters/huaweiads/huaweiadstest/exemplary/nativeThreeImageIncludeIcon.json
+++ b/adapters/huaweiads/huaweiadstest/exemplary/nativeThreeImageIncludeIcon.json
@@ -93,9 +93,7 @@
               "detailedCreativeTypeList": [
                 "904"
               ],
-              "h": 200,
-              "test": 1,
-              "w": 200
+              "test": 1
             }
           ],
           "device": {
diff --git a/adapters/huaweiads/huaweiadstest/supplemental/bad_response_not_native.json b/adapters/huaweiads/huaweiadstest/supplemental/bad_response_not_native.json
index c38bc9f6..14667370 100644
--- a/adapters/huaweiads/huaweiadstest/supplemental/bad_response_not_native.json
+++ b/adapters/huaweiads/huaweiadstest/supplemental/bad_response_not_native.json
@@ -93,9 +93,7 @@
               "test": 1,
               "detailedCreativeTypeList": [
                 "903"
-              ],
-              "h": 200,
-              "w": 200
+              ]
             }
           ],
           "device": {
diff --git a/adapters/response_test.go b/adapters/response_test.go
index 824a295e..0651ff26 100644
--- a/adapters/response_test.go
+++ b/adapters/response_test.go
@@ -8,17 +8,33 @@ import (
 )
 
 func TestCheckResponseStatusCodeForErrors(t *testing.T) {
-	t.Run("bad_input", func(t *testing.T) {
-		err := CheckResponseStatusCodeForErrors(&ResponseData{StatusCode: 400})
-		expectedErr := &errortypes.BadInput{Message: "Unexpected status code: 400. Run with request.debug = 1 for more info"}
-		assert.Equal(t, expectedErr.Error(), err.Error())
-	})
+	testCases := []struct {
+		name           string
+		responseStatus int
+		expectedErr    error
+	}{
+		{
+			name:           "bad_input",
+			responseStatus: 400,
+			expectedErr: &errortypes.BadInput{
+				Message: "Unexpected status code: 400. Run with request.debug = 1 for more info",
+			},
+		},
+		{
+			name:           "internal_server_error",
+			responseStatus: 500,
+			expectedErr: &errortypes.BadServerResponse{
+				Message: "Unexpected status code: 500. Run with request.debug = 1 for more info",
+			},
+		},
+	}
 
-	t.Run("internal_server_error", func(t *testing.T) {
-		err := CheckResponseStatusCodeForErrors(&ResponseData{StatusCode: 500})
-		expectedErrMessage := "Unexpected status code: 500. Run with request.debug = 1 for more info"
-		assert.Equal(t, expectedErrMessage, err.Error())
-	})
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			err := CheckResponseStatusCodeForErrors(&ResponseData{StatusCode: tc.responseStatus})
+			assert.Equal(t, tc.expectedErr, err)
+		})
+	}
 }
 
 func TestIsResponseStatusCodeNoContent(t *testing.T) {
diff --git a/adapters/sovrn/sovrntest/supplemental/adunitcode.json b/adapters/sovrn/sovrntest/supplemental/adunitcode.json
new file mode 100644
index 00000000..47197520
--- /dev/null
+++ b/adapters/sovrn/sovrntest/supplemental/adunitcode.json
@@ -0,0 +1,125 @@
+{
+  "mockBidRequest": {
+    "id": "test-request-id",
+    "imp": [
+      {
+        "id": "test-imp-id",
+        "banner": {
+          "format": [
+            {
+              "w": 300,
+              "h": 250
+            },
+            {
+              "w": 300,
+              "h": 600
+            }
+          ]
+        },
+        "ext": {
+          "bidder": {
+            "tagid": "123456",
+            "adunitcode": "sovrn_auc"
+          }
+        }
+      }
+    ],
+    "device": { },
+    "site": {
+      "domain": "www.publisher.com",
+      "page": "http://www.publisher.com/awesome/site"
+    },
+    "user": {
+      "buyeruid": "test_reader_id"
+    }
+  },
+
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "headers": {
+          "Content-Type": ["application/json"],
+          "Cookie": ["ljt_reader=test_reader_id"]
+        },
+        "uri": "http://sovrn.com/test/endpoint",
+        "body": {
+          "id": "test-request-id",
+          "imp": [
+            {
+              "id": "test-imp-id",
+              "banner": {
+                "format": [
+                  {
+                    "w": 300,
+                    "h": 250
+                  },
+                  {
+                    "w": 300,
+                    "h": 600
+                  }
+                ]
+              },
+              "tagid": "123456",
+              "ext": {
+                "adunitcode": "sovrn_auc",
+                "bidder": {
+                  "tagid": "123456",
+                  "adunitcode": "sovrn_auc"
+                }
+              }
+            }
+          ],
+          "site": {
+            "domain": "www.publisher.com",
+            "page": "http://www.publisher.com/awesome/site"
+          },
+          "user": {
+            "buyeruid": "test_reader_id"
+          },
+          "device": { }
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "id": "test-request-id",
+          "seatbid": [
+            {
+              "bid": [
+                {
+                  "id": "a_449642_554a13d3b9f348fba707cf83f0f63800",
+                  "impid": "test-imp-id",
+                  "price": 3.5,
+                  "nurl": "http://sovrn.com/rtb/impression?bannerid=138743&campaignid=3699&zoneid=449642&cb=69493397&tid=a_449642_554a13d3b9f348fba707cf83f0f63800",
+                  "adm": "some-test-ad",
+                  "w": 300,
+                  "h": 250
+                }
+              ]
+            }
+          ]
+        }
+      }
+    }
+  ],
+
+  "expectedBidResponses": [
+    {
+      "currency": "USD",
+      "bids": [
+        {
+          "bid": {
+            "id": "a_449642_554a13d3b9f348fba707cf83f0f63800",
+            "impid": "test-imp-id",
+            "price": 3.5,
+            "adm": "some-test-ad",
+            "nurl": "http://sovrn.com/rtb/impression?bannerid=138743&campaignid=3699&zoneid=449642&cb=69493397&tid=a_449642_554a13d3b9f348fba707cf83f0f63800",
+            "w": 300,
+            "h": 250
+          },
+          "type": "banner"
+        }
+      ]
+    }
+  ]
+}
diff --git a/adapters/sovrn/sovrntest/supplemental/fpd.json b/adapters/sovrn/sovrntest/supplemental/fpd.json
index 005d159d..e4368c13 100644
--- a/adapters/sovrn/sovrntest/supplemental/fpd.json
+++ b/adapters/sovrn/sovrntest/supplemental/fpd.json
@@ -86,8 +86,7 @@
               "tagid": "123456",
               "ext": {
                 "bidder": {
-                  "tagid": "123456",
-                  "anotherid": "654321"
+                  "tagid": "123456"
                 }
               }
             }
diff --git a/adapters/sovrn/sovrntest/supplemental/invalid-adunitcode.json b/adapters/sovrn/sovrntest/supplemental/invalid-adunitcode.json
new file mode 100644
index 00000000..fdb14371
--- /dev/null
+++ b/adapters/sovrn/sovrntest/supplemental/invalid-adunitcode.json
@@ -0,0 +1,123 @@
+{
+  "mockBidRequest": {
+    "id": "test-request-id",
+    "imp": [
+      {
+        "id": "test-imp-id",
+        "banner": {
+          "format": [
+            {
+              "w": 300,
+              "h": 250
+            },
+            {
+              "w": 300,
+              "h": 600
+            }
+          ]
+        },
+        "ext": {
+          "AdUnitCode": "sovrn_auc",
+          "bidder": {
+            "tagid": "123456"
+          }
+        }
+      }
+    ],
+    "device": { },
+    "site": {
+      "domain": "www.publisher.com",
+      "page": "http://www.publisher.com/awesome/site"
+    },
+    "user": {
+      "buyeruid": "test_reader_id"
+    }
+  },
+
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "headers": {
+          "Content-Type": ["application/json"],
+          "Cookie": ["ljt_reader=test_reader_id"]
+        },
+        "uri": "http://sovrn.com/test/endpoint",
+        "body": {
+          "id": "test-request-id",
+          "imp": [
+            {
+              "id": "test-imp-id",
+              "banner": {
+                "format": [
+                  {
+                    "w": 300,
+                    "h": 250
+                  },
+                  {
+                    "w": 300,
+                    "h": 600
+                  }
+                ]
+              },
+              "tagid": "123456",
+              "ext": {
+                "bidder": {
+                  "tagid": "123456"
+                }
+              }
+            }
+          ],
+          "site": {
+            "domain": "www.publisher.com",
+            "page": "http://www.publisher.com/awesome/site"
+          },
+          "user": {
+            "buyeruid": "test_reader_id"
+          },
+          "device": { }
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "id": "test-request-id",
+          "seatbid": [
+            {
+              "bid": [
+                {
+                  "id": "a_449642_554a13d3b9f348fba707cf83f0f63800",
+                  "impid": "test-imp-id",
+                  "price": 3.5,
+                  "nurl": "http://sovrn.com/rtb/impression?bannerid=138743&campaignid=3699&zoneid=449642&cb=69493397&tid=a_449642_554a13d3b9f348fba707cf83f0f63800",
+                  "adm": "some-test-ad",
+                  "w": 300,
+                  "h": 250
+                }
+              ]
+            }
+          ]
+        }
+      }
+    }
+  ],
+
+  "expectedBidResponses": [
+    {
+      "currency": "USD",
+      "bids": [
+        {
+          "bid": {
+            "id": "a_449642_554a13d3b9f348fba707cf83f0f63800",
+            "impid": "test-imp-id",
+            "price": 3.5,
+            "adm": "some-test-ad",
+            "nurl": "http://sovrn.com/rtb/impression?bannerid=138743&campaignid=3699&zoneid=449642&cb=69493397&tid=a_449642_554a13d3b9f348fba707cf83f0f63800",
+            "w": 300,
+            "h": 250
+          },
+          "type": "banner"
+        }
+      ]
+    }
+  ]
+}
diff --git a/adapters/undertone/undertonetest/exemplary/multi-imp-app-request.json b/adapters/undertone/undertonetest/exemplary/multi-imp-app-request.json
index e6b4a83a..71ad2ae1 100644
--- a/adapters/undertone/undertonetest/exemplary/multi-imp-app-request.json
+++ b/adapters/undertone/undertonetest/exemplary/multi-imp-app-request.json
@@ -54,7 +54,8 @@
           "bidder": {
             "publisherId": 1234,
             "placementId": 123456
-          }
+          },
+          "gpid": "gpid-value"
         }
       }
     ],
@@ -116,7 +117,10 @@
                   4
                 ]
               },
-              "tagid": "123456"
+              "tagid": "123456",
+              "ext": {
+                "gpid": "gpid-value"
+              }
             }
           ],
           "app": {
diff --git a/adapters/undertone/undertonetest/exemplary/multi-imp-site-request.json b/adapters/undertone/undertonetest/exemplary/multi-imp-site-request.json
index 0de38caa..9e59ffbc 100644
--- a/adapters/undertone/undertonetest/exemplary/multi-imp-site-request.json
+++ b/adapters/undertone/undertonetest/exemplary/multi-imp-site-request.json
@@ -20,7 +20,8 @@
           "bidder": {
             "publisherId": 1234,
             "placementId": 12345
-          }
+          },
+          "gpid": "gpid-value"
         }
       },
       {
@@ -87,7 +88,10 @@
                   }
                 ]
               },
-              "tagid": "12345"
+              "tagid": "12345",
+              "ext": {
+                "gpid": "gpid-value"
+              }
             },
             {
               "id": "test-imp-video-id",
diff --git a/config/account_test.go b/config/account_test.go
index 20c60532..31d9796a 100644
--- a/config/account_test.go
+++ b/config/account_test.go
@@ -910,3 +910,49 @@ func TestAccountPriceFloorsValidate(t *testing.T) {
 		})
 	}
 }
+
+func TestIPMaskingValidate(t *testing.T) {
+	tests := []struct {
+		name    string
+		privacy AccountPrivacy
+		want    []error
+	}{
+		{
+			name: "valid",
+			privacy: AccountPrivacy{
+				IPv4Config: IPv4{AnonKeepBits: 1},
+				IPv6Config: IPv6{AnonKeepBits: 0},
+			},
+		},
+		{
+			name: "invalid",
+			privacy: AccountPrivacy{
+				IPv4Config: IPv4{AnonKeepBits: -100},
+				IPv6Config: IPv6{AnonKeepBits: -200},
+			},
+			want: []error{
+				errors.New("bits cannot exceed 32 in ipv4 address, or be less than 0"),
+				errors.New("bits cannot exceed 128 in ipv6 address, or be less than 0"),
+			},
+		},
+		{
+			name: "mixed",
+			privacy: AccountPrivacy{
+				IPv4Config: IPv4{AnonKeepBits: 10},
+				IPv6Config: IPv6{AnonKeepBits: -10},
+			},
+			want: []error{
+				errors.New("bits cannot exceed 128 in ipv6 address, or be less than 0"),
+			},
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			var errs []error
+			errs = tt.privacy.IPv4Config.Validate(errs)
+			errs = tt.privacy.IPv6Config.Validate(errs)
+			assert.ElementsMatch(t, errs, tt.want)
+		})
+	}
+}
diff --git a/config/bidderinfo_test.go b/config/bidderinfo_test.go
index e505d2f8..77625c0c 100644
--- a/config/bidderinfo_test.go
+++ b/config/bidderinfo_test.go
@@ -152,6 +152,14 @@ func TestProcessBidderInfo(t *testing.T) {
 			expectedBidderInfos: nil,
 			expectError:         "error parsing config for bidder bidderA.yaml",
 		},
+		{
+			description: "Invalid alias name",
+			bidderInfos: map[string][]byte{
+				"all.yaml": []byte(testSimpleAliasYAML),
+			},
+			expectedBidderInfos: nil,
+			expectError:         "alias all is a reserved bidder name and cannot be used",
+		},
 		{
 			description: "Valid aliases",
 			bidderInfos: map[string][]byte{
@@ -442,7 +450,6 @@ func TestProcessAliasBidderInfo(t *testing.T) {
 			assert.Equal(t, test.expectedErr, err)
 		} else {
 			assert.Equal(t, test.expectedBidderInfos, bidderInfos)
-			assert.Contains(t, openrtb_ext.CoreBidderNames(), openrtb_ext.BidderName("bidderB"))
 		}
 	}
 }
diff --git a/config/config_test.go b/config/config_test.go
index a35891ba..057ed06e 100644
--- a/config/config_test.go
+++ b/config/config_test.go
@@ -206,6 +206,9 @@ func TestDefaults(t *testing.T) {
 	cmpUnsignedInts(t, "tmax_adjustments.bidder_network_latency_buffer_ms", 0, cfg.TmaxAdjustments.BidderNetworkLatencyBuffer)
 	cmpUnsignedInts(t, "tmax_adjustments.pbs_response_preparation_duration_ms", 0, cfg.TmaxAdjustments.PBSResponsePreparationDuration)
 
+	cmpInts(t, "account_defaults.privacy.ipv6.anon_keep_bits", 56, cfg.AccountDefaults.Privacy.IPv6Config.AnonKeepBits)
+	cmpInts(t, "account_defaults.privacy.ipv4.anon_keep_bits", 24, cfg.AccountDefaults.Privacy.IPv4Config.AnonKeepBits)
+
 	//Assert purpose VendorExceptionMap hash tables were built correctly
 	expectedTCF2 := TCF2{
 		Enabled: true,
@@ -477,6 +480,11 @@ account_defaults:
         use_dynamic_data: true
         max_rules: 120
         max_schema_dims: 5
+    privacy:
+        ipv6:
+            anon_keep_bits: 50
+        ipv4:
+            anon_keep_bits: 20
 tmax_adjustments:
   enabled: true
   bidder_response_duration_min_ms: 700
@@ -583,6 +591,9 @@ func TestFullConfig(t *testing.T) {
 	cmpBools(t, "account_defaults.events_enabled", *cfg.AccountDefaults.EventsEnabled, true)
 	cmpNils(t, "account_defaults.events.enabled", cfg.AccountDefaults.Events.Enabled)
 
+	cmpInts(t, "account_defaults.privacy.ipv6.anon_keep_bits", 50, cfg.AccountDefaults.Privacy.IPv6Config.AnonKeepBits)
+	cmpInts(t, "account_defaults.privacy.ipv4.anon_keep_bits", 20, cfg.AccountDefaults.Privacy.IPv4Config.AnonKeepBits)
+
 	// Assert compression related defaults
 	cmpBools(t, "enable_gzip", false, cfg.EnableGzip)
 	cmpBools(t, "compression.request.enable_gzip", true, cfg.Compression.Request.GZIP)
diff --git a/endpoints/cookie_sync_test.go b/endpoints/cookie_sync_test.go
index 22ecc1fa..45c27f0e 100644
--- a/endpoints/cookie_sync_test.go
+++ b/endpoints/cookie_sync_test.go
@@ -2108,7 +2108,7 @@ func (p *fakePermissions) AuctionActivitiesAllowed(ctx context.Context, bidderCo
 
 func getDefaultActivityConfig(componentName string, allow bool) *config.AccountPrivacy {
 	return &config.AccountPrivacy{
-		AllowActivities: config.AllowActivities{
+		AllowActivities: &config.AllowActivities{
 			SyncUser: config.Activity{
 				Default: ptrutil.ToPtr(true),
 				Rules: []config.ActivityRule{
diff --git a/endpoints/openrtb2/auction_test.go b/endpoints/openrtb2/auction_test.go
index c42209e0..84e5cf4b 100644
--- a/endpoints/openrtb2/auction_test.go
+++ b/endpoints/openrtb2/auction_test.go
@@ -965,19 +965,22 @@ func TestImplicitDNTEndToEnd(t *testing.T) {
 func TestReferer(t *testing.T) {
 	testCases := []struct {
 		description             string
-		givenSitePage           string
-		givenSiteDomain         string
-		givenPublisherDomain    string
 		givenReferer            string
 		expectedDomain          string
 		expectedPage            string
 		expectedPublisherDomain string
+		bidReq                  *openrtb_ext.RequestWrapper
 	}{
 		{
 			description:             "site.page/domain are unchanged when site.page/domain and http referer are not set",
 			expectedDomain:          "",
 			expectedPage:            "",
 			expectedPublisherDomain: "",
+			bidReq: &openrtb_ext.RequestWrapper{BidRequest: &openrtb2.BidRequest{
+				Site: &openrtb2.Site{
+					Publisher: &openrtb2.Publisher{},
+				},
+			}},
 		},
 		{
 			description:             "site.page/domain are derived from referer when neither is set and http referer is set",
@@ -985,39 +988,73 @@ func TestReferer(t *testing.T) {
 			expectedDomain:          "test.somepage.com",
 			expectedPublisherDomain: "somepage.com",
 			expectedPage:            "https://test.somepage.com",
+			bidReq: &openrtb_ext.RequestWrapper{BidRequest: &openrtb2.BidRequest{
+				Site: &openrtb2.Site{
+					Publisher: &openrtb2.Publisher{},
+				},
+			}},
 		},
 		{
 			description:             "site.domain is derived from site.page when site.page is set and http referer is not set",
-			givenSitePage:           "https://test.somepage.com",
 			expectedDomain:          "test.somepage.com",
 			expectedPublisherDomain: "somepage.com",
 			expectedPage:            "https://test.somepage.com",
+			bidReq: &openrtb_ext.RequestWrapper{BidRequest: &openrtb2.BidRequest{
+				Site: &openrtb2.Site{
+					Page:      "https://test.somepage.com",
+					Publisher: &openrtb2.Publisher{},
+				},
+			}},
 		},
 		{
 			description:             "site.domain is derived from http referer when site.page and http referer are set",
-			givenSitePage:           "https://test.somepage.com",
 			givenReferer:            "http://test.com",
 			expectedDomain:          "test.com",
 			expectedPublisherDomain: "test.com",
 			expectedPage:            "https://test.somepage.com",
+			bidReq: &openrtb_ext.RequestWrapper{BidRequest: &openrtb2.BidRequest{
+				Site: &openrtb2.Site{
+					Page:      "https://test.somepage.com",
+					Publisher: &openrtb2.Publisher{},
+				},
+			}},
 		},
 		{
 			description:             "site.page/domain are unchanged when site.page/domain and http referer are set",
-			givenSitePage:           "https://test.somepage.com",
-			givenSiteDomain:         "some.domain.com",
 			givenReferer:            "http://test.com",
 			expectedDomain:          "some.domain.com",
 			expectedPublisherDomain: "test.com",
 			expectedPage:            "https://test.somepage.com",
+			bidReq: &openrtb_ext.RequestWrapper{BidRequest: &openrtb2.BidRequest{
+				Site: &openrtb2.Site{
+					Domain:    "some.domain.com",
+					Page:      "https://test.somepage.com",
+					Publisher: &openrtb2.Publisher{},
+				},
+			}},
 		},
 		{
 			description:             "Publisher domain shouldn't be overrwriten if already set",
-			givenSitePage:           "https://test.somepage.com",
-			givenSiteDomain:         "",
-			givenPublisherDomain:    "differentpage.com",
 			expectedDomain:          "test.somepage.com",
 			expectedPublisherDomain: "differentpage.com",
 			expectedPage:            "https://test.somepage.com",
+			bidReq: &openrtb_ext.RequestWrapper{BidRequest: &openrtb2.BidRequest{
+				Site: &openrtb2.Site{
+					Domain: "",
+					Page:   "https://test.somepage.com",
+					Publisher: &openrtb2.Publisher{
+						Domain: "differentpage.com",
+					},
+				},
+			}},
+		},
+		{
+			description:             "request.site is nil",
+			givenReferer:            "http://test.com",
+			expectedDomain:          "test.com",
+			expectedPublisherDomain: "test.com",
+			expectedPage:            "http://test.com",
+			bidReq:                  &openrtb_ext.RequestWrapper{BidRequest: &openrtb2.BidRequest{}},
 		},
 	}
 
@@ -1025,20 +1062,12 @@ func TestReferer(t *testing.T) {
 		httpReq := httptest.NewRequest("POST", "/openrtb2/auction", strings.NewReader(validRequest(t, "site.json")))
 		httpReq.Header.Set("Referer", test.givenReferer)
 
-		bidReq := &openrtb_ext.RequestWrapper{BidRequest: &openrtb2.BidRequest{
-			Site: &openrtb2.Site{
-				Domain:    test.givenSiteDomain,
-				Page:      test.givenSitePage,
-				Publisher: &openrtb2.Publisher{Domain: test.givenPublisherDomain},
-			},
-		}}
-
-		setSiteImplicitly(httpReq, bidReq)
+		setSiteImplicitly(httpReq, test.bidReq)
 
-		assert.NotNil(t, bidReq.Site, test.description)
-		assert.Equal(t, test.expectedDomain, bidReq.Site.Domain, test.description)
-		assert.Equal(t, test.expectedPage, bidReq.Site.Page, test.description)
-		assert.Equal(t, test.expectedPublisherDomain, bidReq.Site.Publisher.Domain, test.description)
+		assert.NotNil(t, test.bidReq.Site, test.description)
+		assert.Equal(t, test.expectedDomain, test.bidReq.Site.Domain, test.description)
+		assert.Equal(t, test.expectedPage, test.bidReq.Site.Page, test.description)
+		assert.Equal(t, test.expectedPublisherDomain, test.bidReq.Site.Publisher.Domain, test.description)
 	}
 }
 
diff --git a/endpoints/openrtb2/video_auction_test.go b/endpoints/openrtb2/video_auction_test.go
index a32afd24..3c2528e2 100644
--- a/endpoints/openrtb2/video_auction_test.go
+++ b/endpoints/openrtb2/video_auction_test.go
@@ -1299,8 +1299,8 @@ func mockDepsInvalidPrivacy(t *testing.T, ex *mockExchangeVideo) *endpointDeps {
 		&mockAccountFetcher{data: mockVideoAccountData},
 		&config.Configuration{MaxRequestSize: maxSize,
 			AccountDefaults: config.Account{
-				Privacy: &config.AccountPrivacy{
-					AllowActivities: config.AllowActivities{
+				Privacy: config.AccountPrivacy{
+					AllowActivities: &config.AllowActivities{
 						TransmitPreciseGeo: config.Activity{Rules: []config.ActivityRule{
 							{Condition: config.ActivityCondition{ComponentName: []string{"bidderA.BidderB.bidderC"}}},
 						}},
diff --git a/exchange/exchangetest/request-ext-prebid-filtering.json b/exchange/exchangetest/request-ext-prebid-filtering.json
index 4355f517..8e85d8ed 100644
--- a/exchange/exchangetest/request-ext-prebid-filtering.json
+++ b/exchange/exchangetest/request-ext-prebid-filtering.json
@@ -65,7 +65,10 @@
                         "renderers": [
                             {
                                 "name": "test-name",
-                                "version": "test-version"
+                                "version": "test-version",
+                                "data" : {
+                                    "complex": "data"
+                                }
                             }
                         ]
                     }
@@ -121,7 +124,10 @@
                                 "renderers": [
                                     {
                                         "name": "test-name",
-                                        "version": "test-version"
+                                        "version": "test-version",
+                                        "data": {
+                                            "complex": "data"
+                                        }
                                     }
                                 ]
                             }
diff --git a/exchange/utils_test.go b/exchange/utils_test.go
index 63ca54c2..1ab86fcd 100644
--- a/exchange/utils_test.go
+++ b/exchange/utils_test.go
@@ -4272,7 +4272,7 @@ func TestCleanOpenRTBRequestsActivities(t *testing.T) {
 	testCases := []struct {
 		name                 string
 		req                  *openrtb2.BidRequest
-		privacyConfig        *config.AccountPrivacy
+		privacyConfig        config.AccountPrivacy
 		componentName        string
 		allow                bool
 		expectedReqNumber    int
@@ -4364,7 +4364,7 @@ func TestCleanOpenRTBRequestsActivities(t *testing.T) {
 	}
 
 	for _, test := range testCases {
-		activities, err := privacy.NewActivityControl(test.privacyConfig)
+		activities, err := privacy.NewActivityControl(&test.privacyConfig)
 		assert.NoError(t, err, "")
 		auctionReq := AuctionRequest{
 			BidRequestWrapper: &openrtb_ext.RequestWrapper{BidRequest: test.req},
@@ -4410,33 +4410,33 @@ func buildDefaultActivityConfig(componentName string, allow bool) config.Activit
 	}
 }
 
-func getFetchBidsActivityConfig(componentName string, allow bool) *config.AccountPrivacy {
-	return &config.AccountPrivacy{
-		AllowActivities: config.AllowActivities{
+func getFetchBidsActivityConfig(componentName string, allow bool) config.AccountPrivacy {
+	return config.AccountPrivacy{
+		AllowActivities: &config.AllowActivities{
 			FetchBids: buildDefaultActivityConfig(componentName, allow),
 		},
 	}
 }
 
-func getTransmitUFPDActivityConfig(componentName string, allow bool) *config.AccountPrivacy {
-	return &config.AccountPrivacy{
-		AllowActivities: config.AllowActivities{
+func getTransmitUFPDActivityConfig(componentName string, allow bool) config.AccountPrivacy {
+	return config.AccountPrivacy{
+		AllowActivities: &config.AllowActivities{
 			TransmitUserFPD: buildDefaultActivityConfig(componentName, allow),
 		},
 	}
 }
 
-func getTransmitPreciseGeoActivityConfig(componentName string, allow bool) *config.AccountPrivacy {
-	return &config.AccountPrivacy{
-		AllowActivities: config.AllowActivities{
+func getTransmitPreciseGeoActivityConfig(componentName string, allow bool) config.AccountPrivacy {
+	return config.AccountPrivacy{
+		AllowActivities: &config.AllowActivities{
 			TransmitPreciseGeo: buildDefaultActivityConfig(componentName, allow),
 		},
 	}
 }
 
-func getTransmitTIDActivityConfig(componentName string, allow bool) *config.AccountPrivacy {
-	return &config.AccountPrivacy{
-		AllowActivities: config.AllowActivities{
+func getTransmitTIDActivityConfig(componentName string, allow bool) config.AccountPrivacy {
+	return config.AccountPrivacy{
+		AllowActivities: &config.AllowActivities{
 			TransmitTids: buildDefaultActivityConfig(componentName, allow),
 		},
 	}
diff --git a/floors/enforce_test.go b/floors/enforce_test.go
index bb5d4434..725ac22b 100644
--- a/floors/enforce_test.go
+++ b/floors/enforce_test.go
@@ -196,6 +196,71 @@ func TestEnforceFloorToBids(t *testing.T) {
 			},
 			expErrs: []error{},
 		},
+		{
+			name: "Bids with price less than bidfloor with floorsPrecision",
+			args: args{
+				bidRequestWrapper: func() *openrtb_ext.RequestWrapper {
+					bw := openrtb_ext.RequestWrapper{
+						BidRequest: &openrtb2.BidRequest{
+							ID: "some-request-id",
+							Imp: []openrtb2.Imp{
+								{ID: "some-impression-id-1", BidFloor: 1, BidFloorCur: "USD"},
+								{ID: "some-impression-id-2", BidFloor: 2, BidFloorCur: "USD"},
+							},
+						},
+					}
+					bw.RebuildRequest()
+					return &bw
+				}(),
+				seatBids: map[openrtb_ext.BidderName]*entities.PbsOrtbSeatBid{
+					"pubmatic": {
+						Bids: []*entities.PbsOrtbBid{
+							{Bid: &openrtb2.Bid{ID: "some-bid-1", Price: 0.998, ImpID: "some-impression-id-1"}},
+							{Bid: &openrtb2.Bid{ID: "some-bid-2", Price: 1.5, DealID: "deal_Id", ImpID: "some-impression-id-2"}},
+						},
+						Seat:     "pubmatic",
+						Currency: "USD",
+					},
+					"appnexus": {
+						Bids: []*entities.PbsOrtbBid{
+							{Bid: &openrtb2.Bid{ID: "some-bid-11", Price: 0.8, ImpID: "some-impression-id-1"}},
+							{Bid: &openrtb2.Bid{ID: "some-bid-12", Price: 2.2, ImpID: "some-impression-id-2"}},
+						},
+						Seat:     "appnexus",
+						Currency: "USD",
+					},
+				},
+				conversions:       currency.Conversions(convert{}),
+				enforceDealFloors: false,
+			},
+			expEligibleBids: map[openrtb_ext.BidderName]*entities.PbsOrtbSeatBid{
+				"pubmatic": {
+					Bids: []*entities.PbsOrtbBid{
+						{Bid: &openrtb2.Bid{ID: "some-bid-1", Price: 0.998, ImpID: "some-impression-id-1"}},
+						{Bid: &openrtb2.Bid{ID: "some-bid-2", Price: 1.5, DealID: "deal_Id", ImpID: "some-impression-id-2"}},
+					},
+					Seat:     "pubmatic",
+					Currency: "USD",
+				},
+				"appnexus": {
+					Bids: []*entities.PbsOrtbBid{
+						{Bid: &openrtb2.Bid{ID: "some-bid-12", Price: 2.2, ImpID: "some-impression-id-2"}},
+					},
+					Seat:     "appnexus",
+					Currency: "USD",
+				},
+			},
+			expRejectedBids: []*entities.PbsOrtbSeatBid{
+				{
+					Seat:     "appnexus",
+					Currency: "USD",
+					Bids: []*entities.PbsOrtbBid{
+						{Bid: &openrtb2.Bid{ID: "some-bid-11", Price: 0.8, ImpID: "some-impression-id-1"}},
+					},
+				},
+			},
+			expErrs: []error{},
+		},
 		{
 			name: "Bids with different currency with enforceDealFloor true",
 			args: args{
@@ -1154,7 +1219,6 @@ func TestUpdateEnforcePBS(t *testing.T) {
 		t.Run(tt.name, func(t *testing.T) {
 			got := updateEnforcePBS(tt.args.enforceFloors, tt.args.reqExt)
 			assert.Equal(t, tt.want, got, tt.name)
-
 		})
 	}
 }
diff --git a/hooks/hookexecution/mocks_test.go b/hooks/hookexecution/mocks_test.go
index e8670ff8..ff543c38 100644
--- a/hooks/hookexecution/mocks_test.go
+++ b/hooks/hookexecution/mocks_test.go
@@ -131,7 +131,7 @@ func (e mockTimeoutHook) HandleProcessedAuctionHook(_ context.Context, _ hooksta
 	time.Sleep(20 * time.Millisecond)
 	c := hookstage.ChangeSet[hookstage.ProcessedAuctionRequestPayload]{}
 	c.AddMutation(func(payload hookstage.ProcessedAuctionRequestPayload) (hookstage.ProcessedAuctionRequestPayload, error) {
-		payload.BidRequest.User.CustomData = "some-custom-data"
+		payload.RequestWrapper.User.CustomData = "some-custom-data"
 		return payload, nil
 	}, hookstage.MutationUpdate, "bidRequest", "user.customData")
 
@@ -305,12 +305,20 @@ func (e mockUpdateBidRequestHook) HandleProcessedAuctionHook(_ context.Context,
 	c := hookstage.ChangeSet[hookstage.ProcessedAuctionRequestPayload]{}
 	c.AddMutation(
 		func(payload hookstage.ProcessedAuctionRequestPayload) (hookstage.ProcessedAuctionRequestPayload, error) {
-			payload.BidRequest.User.Yob = 2000
+			payload.RequestWrapper.User.Yob = 2000
+			userExt, err := payload.RequestWrapper.GetUserExt()
+			if err != nil {
+				return payload, err
+			}
+			newPrebidExt := &openrtb_ext.ExtUserPrebid{
+				BuyerUIDs: map[string]string{"some": "id"},
+			}
+			userExt.SetPrebid(newPrebidExt)
 			return payload, nil
 		}, hookstage.MutationUpdate, "bidRequest", "user.yob",
 	).AddMutation(
 		func(payload hookstage.ProcessedAuctionRequestPayload) (hookstage.ProcessedAuctionRequestPayload, error) {
-			payload.BidRequest.User.Consent = "true"
+			payload.RequestWrapper.User.Consent = "true"
 			return payload, nil
 		}, hookstage.MutationUpdate, "bidRequest", "user.consent",
 	)
diff --git a/openrtb_ext/bidders_test.go b/openrtb_ext/bidders_test.go
index eb60f452..2176c28e 100644
--- a/openrtb_ext/bidders_test.go
+++ b/openrtb_ext/bidders_test.go
@@ -2,7 +2,10 @@ package openrtb_ext
 
 import (
 	"encoding/json"
+	"errors"
+	"os"
 	"testing"
+	"testing/fstest"
 
 	"github.com/stretchr/testify/assert"
 	"github.com/xeipuuv/gojsonschema"
@@ -126,8 +129,154 @@ func TestIsBidderNameReserved(t *testing.T) {
 	}
 }
 
-func TestCoreBidderNames(t *testing.T) {
-	bidderA := "BidderA"
-	SetAliasBidderName(BidderName(bidderA))
-	assert.Contains(t, CoreBidderNames(), BidderName(bidderA))
+func TestSetAliasBidderName(t *testing.T) {
+	parentBidder := BidderName("pBidder")
+	existingCoreBidderNames := coreBidderNames
+
+	testCases := []struct {
+		aliasBidderName string
+		err             error
+	}{
+		{"aBidder", nil},
+		{"all", errors.New("alias all is a reserved bidder name and cannot be used")},
+	}
+
+	for _, test := range testCases {
+		err := SetAliasBidderName(test.aliasBidderName, parentBidder)
+		if err != nil {
+			assert.Equal(t, test.err, err)
+		} else {
+			assert.Contains(t, CoreBidderNames(), BidderName(test.aliasBidderName))
+			assert.Contains(t, aliasBidderToParent, BidderName(test.aliasBidderName))
+		}
+	}
+
+	//reset package variables to not interfere with other test cases. Example - TestBidderParamSchemas
+	coreBidderNames = existingCoreBidderNames
+	aliasBidderToParent = map[BidderName]BidderName{}
+}
+
+type mockParamsHelper struct {
+	fs              fstest.MapFS
+	absFilePath     string
+	absPathErr      error
+	schemaLoaderErr error
+	readFileErr     error
+}
+
+func (m *mockParamsHelper) readDir(name string) ([]os.DirEntry, error) {
+	return m.fs.ReadDir(name)
+}
+
+func (m *mockParamsHelper) readFile(name string) ([]byte, error) {
+	if m.readFileErr != nil {
+		return nil, m.readFileErr
+	}
+	return m.fs.ReadFile(name)
+}
+
+func (m *mockParamsHelper) newReferenceLoader(source string) gojsonschema.JSONLoader {
+	return nil
+}
+
+func (m *mockParamsHelper) newSchema(l gojsonschema.JSONLoader) (*gojsonschema.Schema, error) {
+	return nil, m.schemaLoaderErr
+}
+
+func (m *mockParamsHelper) abs(path string) (string, error) {
+	return m.absFilePath, m.absPathErr
+}
+
+func TestNewBidderParamsValidator(t *testing.T) {
+	testCases := []struct {
+		description     string
+		paramsValidator mockParamsHelper
+		dir             string
+		expectedErr     error
+	}{
+		{
+			description: "Valid case",
+			paramsValidator: mockParamsHelper{
+				fs: fstest.MapFS{
+					"test/appnexus.json": {
+						Data: []byte("{}"),
+					},
+				},
+			},
+			dir: "test",
+		},
+		{
+			description:     "failed to read directory",
+			paramsValidator: mockParamsHelper{},
+			dir:             "t",
+			expectedErr:     errors.New("Failed to read JSON schemas from directory t. open t: file does not exist"),
+		},
+		{
+			description: "file name does not match the bidder name",
+			paramsValidator: mockParamsHelper{
+				fs: fstest.MapFS{
+					"test/anyBidder.json": {
+						Data: []byte("{}"),
+					},
+				},
+			},
+			dir:         "test",
+			expectedErr: errors.New("File test/anyBidder.json does not match a valid BidderName."),
+		},
+		{
+			description: "abs file path error",
+			paramsValidator: mockParamsHelper{
+				fs: fstest.MapFS{
+					"test/appnexus.json": {
+						Data: []byte("{}"),
+					},
+				},
+				absFilePath: "test/app.json",
+				absPathErr:  errors.New("any abs error"),
+			},
+			dir:         "test",
+			expectedErr: errors.New("Failed to get an absolute representation of the path: test/app.json, any abs error"),
+		},
+		{
+			description: "schema loader error",
+			paramsValidator: mockParamsHelper{
+				fs: fstest.MapFS{
+					"test/appnexus.json": {
+						Data: []byte("{}"),
+					},
+				},
+				schemaLoaderErr: errors.New("any schema loader error"),
+			},
+			dir:         "test",
+			expectedErr: errors.New("Failed to load json schema at : any schema loader error"),
+		},
+		{
+			description: "read file error",
+			paramsValidator: mockParamsHelper{
+				fs: fstest.MapFS{
+					"test/appnexus.json": {
+						Data: []byte("{}"),
+					},
+				},
+				readFileErr: errors.New("any read file error"),
+			},
+			dir:         "test",
+			expectedErr: errors.New("Failed to read file test/appnexus.json: any read file error"),
+		},
+	}
+
+	for _, test := range testCases {
+		t.Run(test.description, func(t *testing.T) {
+			aliasBidderToParent = map[BidderName]BidderName{"rubicon": "appnexus"}
+			paramsValidator = &test.paramsValidator
+			bidderValidator, err := NewBidderParamsValidator(test.dir)
+			if test.expectedErr == nil {
+				assert.NoError(t, err)
+				assert.Contains(t, bidderValidator.Schema("appnexus"), "{}")
+				assert.Contains(t, bidderValidator.Schema("rubicon"), "{}")
+			} else {
+				assert.Equal(t, err, test.expectedErr)
+			}
+		})
+	}
 }
diff --git a/openrtb_ext/bidders_validate_test.go b/openrtb_ext/bidders_validate_test.go
index c9528536..33148d1e 100644
--- a/openrtb_ext/bidders_validate_test.go
+++ b/openrtb_ext/bidders_validate_test.go
@@ -24,9 +24,6 @@ var validator BidderParamValidator
 // returns valid JSON for all known CoreBidderNames.
 func TestBidderParamSchemas(t *testing.T) {
 	for _, bidderName := range CoreBidderNames() {
-		if _, ok := GetAliasBidderNamesSet()[bidderName]; ok {
-			continue
-		}
 		schema := validator.Schema(bidderName)
 		if schema == "" {
 			t.Errorf("No schema exists for bidder %s. Does static/bidder-params/%s.json exist?", bidderName, bidderName)
diff --git a/privacy/activitycontrol_test.go b/privacy/activitycontrol_test.go
index 7927014a..db5d588d 100644
--- a/privacy/activitycontrol_test.go
+++ b/privacy/activitycontrol_test.go
@@ -12,20 +12,20 @@ import (
 func TestNewActivityControl(t *testing.T) {
 	testCases := []struct {
 		name            string
-		privacyConf     *config.AccountPrivacy
+		privacyConf     config.AccountPrivacy
 		activityControl ActivityControl
 		err             error
 	}{
 		{
-			name:            "privacy_config_is_nil",
-			privacyConf:     nil,
+			name:            "privacy_config_is_empty",
+			privacyConf:     config.AccountPrivacy{},
 			activityControl: ActivityControl{plans: nil},
 			err:             nil,
 		},
 		{
 			name: "privacy_config_is_specified_and_correct",
-			privacyConf: &config.AccountPrivacy{
-				AllowActivities: config.AllowActivities{
+			privacyConf: config.AccountPrivacy{
+				AllowActivities: &config.AllowActivities{
 					SyncUser:                 getDefaultActivityConfig(),
 					FetchBids:                getDefaultActivityConfig(),
 					EnrichUserFPD:            getDefaultActivityConfig(),
@@ -50,8 +50,8 @@ func TestNewActivityControl(t *testing.T) {
 		},
 		{
 			name: "privacy_config_is_specified_and_SyncUser_is_incorrect",
-			privacyConf: &config.AccountPrivacy{
-				AllowActivities: config.AllowActivities{
+			privacyConf: config.AccountPrivacy{
+				AllowActivities: &config.AllowActivities{
 					SyncUser: getIncorrectActivityConfig(),
 				},
 			},
@@ -60,8 +60,8 @@ func TestNewActivityControl(t *testing.T) {
 		},
 		{
 			name: "privacy_config_is_specified_and_FetchBids_is_incorrect",
-			privacyConf: &config.AccountPrivacy{
-				AllowActivities: config.AllowActivities{
+			privacyConf: config.AccountPrivacy{
+				AllowActivities: &config.AllowActivities{
 					FetchBids: getIncorrectActivityConfig(),
 				},
 			},
@@ -70,8 +70,8 @@ func TestNewActivityControl(t *testing.T) {
 		},
 		{
 			name: "privacy_config_is_specified_and_EnrichUserFPD_is_incorrect",
-			privacyConf: &config.AccountPrivacy{
-				AllowActivities: config.AllowActivities{
+			privacyConf: config.AccountPrivacy{
+				AllowActivities: &config.AllowActivities{
 					EnrichUserFPD: getIncorrectActivityConfig(),
 				},
 			},
@@ -80,8 +80,8 @@ func TestNewActivityControl(t *testing.T) {
 		},
 		{
 			name: "privacy_config_is_specified_and_ReportAnalytics_is_incorrect",
-			privacyConf: &config.AccountPrivacy{
-				AllowActivities: config.AllowActivities{
+			privacyConf: config.AccountPrivacy{
+				AllowActivities: &config.AllowActivities{
 					ReportAnalytics: getIncorrectActivityConfig(),
 				},
 			},
@@ -90,8 +90,8 @@ func TestNewActivityControl(t *testing.T) {
 		},
 		{
 			name: "privacy_config_is_specified_and_TransmitUserFPD_is_incorrect",
-			privacyConf: &config.AccountPrivacy{
-				AllowActivities: config.AllowActivities{
+			privacyConf: config.AccountPrivacy{
+				AllowActivities: &config.AllowActivities{
 					TransmitUserFPD: getIncorrectActivityConfig(),
 				},
 			},
@@ -100,8 +100,8 @@ func TestNewActivityControl(t *testing.T) {
 		},
 		{
 			name: "privacy_config_is_specified_and_TransmitPreciseGeo_is_incorrect",
-			privacyConf: &config.AccountPrivacy{
-				AllowActivities: config.AllowActivities{
+			privacyConf: config.AccountPrivacy{
+				AllowActivities: &config.AllowActivities{
 					TransmitPreciseGeo: getIncorrectActivityConfig(),
 				},
 			},
@@ -110,8 +110,8 @@ func TestNewActivityControl(t *testing.T) {
 		},
 		{
 			name: "privacy_config_is_specified_and_TransmitUniqueRequestIds_is_incorrect",
-			privacyConf: &config.AccountPrivacy{
-				AllowActivities: config.AllowActivities{
+			privacyConf: config.AccountPrivacy{
+				AllowActivities: &config.AllowActivities{
 					TransmitUniqueRequestIds: getIncorrectActivityConfig(),
 				},
 			},
@@ -120,8 +120,8 @@ func TestNewActivityControl(t *testing.T) {
 		},
 		{
 			name: "privacy_config_is_specified_and_TransmitTids_is_incorrect",
-			privacyConf: &config.AccountPrivacy{
-				AllowActivities: config.AllowActivities{
+			privacyConf: config.AccountPrivacy{
+				AllowActivities: &config.AllowActivities{
 					TransmitTids: getIncorrectActivityConfig(),
 				},
 			},
@@ -132,7 +132,7 @@ func TestNewActivityControl(t *testing.T) {
 
 	for _, test := range testCases {
 		t.Run(test.name, func(t *testing.T) {
-			actualAC, actualErr := NewActivityControl(test.privacyConf)
+			actualAC, actualErr := NewActivityControl(&test.privacyConf)
 			if test.err == nil {
 				assert.Equal(t, test.activityControl, actualAC)
 				assert.NoError(t, actualErr)
diff --git a/privacy/enforcement_test.go b/privacy/enforcement_test.go
index be1ab0dc..a97779eb 100644
--- a/privacy/enforcement_test.go
+++ b/privacy/enforcement_test.go
@@ -76,8 +76,8 @@ func TestApplyGDPR(t *testing.T) {
 				LMT:     true,
 			},
 			expectedDeviceID:   ScrubStrategyDeviceIDAll,
-			expectedDeviceIPv4: ScrubStrategyIPV4Lowest8,
-			expectedDeviceIPv6: ScrubStrategyIPV6Lowest32,
+			expectedDeviceIPv4: ScrubStrategyIPV4Subnet,
+			expectedDeviceIPv6: ScrubStrategyIPV6Subnet,
 			expectedDeviceGeo:  ScrubStrategyGeoFull,
 			expectedUser:       ScrubStrategyUserIDAndDemographic,
 			expectedUserGeo:    ScrubStrategyGeoFull,
@@ -92,8 +92,8 @@ func TestApplyGDPR(t *testing.T) {
 				LMT:     false,
 			},
 			expectedDeviceID:   ScrubStrategyDeviceIDAll,
-			expectedDeviceIPv4: ScrubStrategyIPV4Lowest8,
-			expectedDeviceIPv6: ScrubStrategyIPV6Lowest16,
+			expectedDeviceIPv4: ScrubStrategyIPV4Subnet,
+			expectedDeviceIPv6: ScrubStrategyIPV6Subnet,
 			expectedDeviceGeo:  ScrubStrategyGeoReducedPrecision,
 			expectedUser:       ScrubStrategyUserIDAndDemographic,
 			expectedUserGeo:    ScrubStrategyGeoReducedPrecision,
@@ -108,8 +108,8 @@ func TestApplyGDPR(t *testing.T) {
 				LMT:     false,
 			},
 			expectedDeviceID:   ScrubStrategyDeviceIDAll,
-			expectedDeviceIPv4: ScrubStrategyIPV4Lowest8,
-			expectedDeviceIPv6: ScrubStrategyIPV6Lowest32,
+			expectedDeviceIPv4: ScrubStrategyIPV4Subnet,
+			expectedDeviceIPv6: ScrubStrategyIPV6Subnet,
 			expectedDeviceGeo:  ScrubStrategyGeoFull,
 			expectedUser:       ScrubStrategyUserIDAndDemographic,
 			expectedUserGeo:    ScrubStrategyGeoFull,
@@ -124,8 +124,8 @@ func TestApplyGDPR(t *testing.T) {
 				LMT:     false,
 			},
 			expectedDeviceID:   ScrubStrategyDeviceIDAll,
-			expectedDeviceIPv4: ScrubStrategyIPV4Lowest8,
-			expectedDeviceIPv6: ScrubStrategyIPV6Lowest16,
+			expectedDeviceIPv4: ScrubStrategyIPV4Subnet,
+			expectedDeviceIPv6: ScrubStrategyIPV6Subnet,
 			expectedDeviceGeo:  ScrubStrategyGeoReducedPrecision,
 			expectedUser:       ScrubStrategyUserIDAndDemographic,
 			expectedUserGeo:    ScrubStrategyGeoReducedPrecision,
@@ -156,8 +156,8 @@ func TestApplyGDPR(t *testing.T) {
 				LMT:     false,
 			},
 			expectedDeviceID:   ScrubStrategyDeviceIDNone,
-			expectedDeviceIPv4: ScrubStrategyIPV4Lowest8,
-			expectedDeviceIPv6: ScrubStrategyIPV6Lowest16,
+			expectedDeviceIPv4: ScrubStrategyIPV4Subnet,
+			expectedDeviceIPv6: ScrubStrategyIPV6Subnet,
 			expectedDeviceGeo:  ScrubStrategyGeoReducedPrecision,
 			expectedUser:       ScrubStrategyUserNone,
 			expectedUserGeo:    ScrubStrategyGeoReducedPrecision,
@@ -172,8 +172,8 @@ func TestApplyGDPR(t *testing.T) {
 				LMT:     true,
 			},
 			expectedDeviceID:   ScrubStrategyDeviceIDAll,
-			expectedDeviceIPv4: ScrubStrategyIPV4Lowest8,
-			expectedDeviceIPv6: ScrubStrategyIPV6Lowest16,
+			expectedDeviceIPv4: ScrubStrategyIPV4Subnet,
+			expectedDeviceIPv6: ScrubStrategyIPV6Subnet,
 			expectedDeviceGeo:  ScrubStrategyGeoReducedPrecision,
 			expectedUser:       ScrubStrategyUserIDAndDemographic,
 			expectedUserGeo:    ScrubStrategyGeoReducedPrecision,
@@ -188,8 +188,8 @@ func TestApplyGDPR(t *testing.T) {
 				LMT:     false,
 			},
 			expectedDeviceID:   ScrubStrategyDeviceIDAll,
-			expectedDeviceIPv4: ScrubStrategyIPV4Lowest8,
-			expectedDeviceIPv6: ScrubStrategyIPV6Lowest32,
+			expectedDeviceIPv4: ScrubStrategyIPV4Subnet,
+			expectedDeviceIPv6: ScrubStrategyIPV6Subnet,
 			expectedDeviceGeo:  ScrubStrategyGeoFull,
 			expectedUser:       ScrubStrategyUserIDAndDemographic,
 			expectedUserGeo:    ScrubStrategyGeoFull,
@@ -329,7 +329,7 @@ func TestApplyToggle(t *testing.T) {
 				m.On("ScrubUser", req.User, ScrubStrategyUserIDAndDemographic, ScrubStrategyGeoFull).Return(replacedUser).Once()
 			}
 			if test.expectedScrubDeviceExecuted {
-				m.On("ScrubDevice", req.Device, ScrubStrategyDeviceIDAll, ScrubStrategyIPV4Lowest8, ScrubStrategyIPV6Lowest32, ScrubStrategyGeoFull).Return(replacedDevice).Once()
+				m.On("ScrubDevice", req.Device, ScrubStrategyDeviceIDAll, ScrubStrategyIPV4Subnet, ScrubStrategyIPV6Subnet, ScrubStrategyGeoFull).Return(replacedDevice).Once()
 			}
 
 			test.enforcement.apply(req, m)
diff --git a/privacy/scrubber_test.go b/privacy/scrubber_test.go
index 0691b2c7..59e593fc 100644
--- a/privacy/scrubber_test.go
+++ b/privacy/scrubber_test.go
@@ -2,6 +2,7 @@ package privacy
 
 import (
 	"encoding/json"
+	"github.com/prebid/prebid-server/config"
 	"testing"
 
 	"github.com/prebid/openrtb/v19/openrtb2"
@@ -38,12 +39,12 @@ func TestScrubDevice(t *testing.T) {
 				MACMD5:   "",
 				IFA:      "",
 				IP:       "1.2.3.0",
-				IPv6:     "2001:0db8:0000:0000:0000:ff00:0:0",
+				IPv6:     "2001:1db8:2233:4400::",
 				Geo:      &openrtb2.Geo{},
 			},
 			id:   ScrubStrategyDeviceIDAll,
-			ipv4: ScrubStrategyIPV4Lowest8,
-			ipv6: ScrubStrategyIPV6Lowest32,
+			ipv4: ScrubStrategyIPV4Subnet,
+			ipv6: ScrubStrategyIPV6Subnet,
 			geo:  ScrubStrategyGeoFull,
 		},
 		{
@@ -57,7 +58,7 @@ func TestScrubDevice(t *testing.T) {
 				MACMD5:   "",
 				IFA:      "",
 				IP:       "1.2.3.4",
-				IPv6:     "2001:0db8:0000:0000:0000:ff00:0042:8329",
+				IPv6:     "2001:1db8:2233:4455:6677:ff00:0042:8329",
 				Geo:      device.Geo,
 			},
 			id:   ScrubStrategyDeviceIDAll,
@@ -76,16 +77,16 @@ func TestScrubDevice(t *testing.T) {
 				MACMD5:   "anyMACMD5",
 				IFA:      "anyIFA",
 				IP:       "1.2.3.0",
-				IPv6:     "2001:0db8:0000:0000:0000:ff00:0042:8329",
+				IPv6:     "2001:1db8:2233:4455:6677:ff00:0042:8329",
 				Geo:      device.Geo,
 			},
 			id:   ScrubStrategyDeviceIDNone,
-			ipv4: ScrubStrategyIPV4Lowest8,
+			ipv4: ScrubStrategyIPV4Subnet,
 			ipv6: ScrubStrategyIPV6None,
 			geo:  ScrubStrategyGeoNone,
 		},
 		{
-			description: "Isolated - IPv6 - Lowest 16",
+			description: "Isolated - IPv6",
 			expected: &openrtb2.Device{
 				DIDMD5:   "anyDIDMD5",
 				DIDSHA1:  "anyDIDSHA1",
@@ -95,31 +96,12 @@ func TestScrubDevice(t *testing.T) {
 				MACMD5:   "anyMACMD5",
 				IFA:      "anyIFA",
 				IP:       "1.2.3.4",
-				IPv6:     "2001:0db8:0000:0000:0000:ff00:0042:0",
+				IPv6:     "2001:1db8:2233:4400::",
 				Geo:      device.Geo,
 			},
 			id:   ScrubStrategyDeviceIDNone,
 			ipv4: ScrubStrategyIPV4None,
-			ipv6: ScrubStrategyIPV6Lowest16,
-			geo:  ScrubStrategyGeoNone,
-		},
-		{
-			description: "Isolated - IPv6 - Lowest 32",
-			expected: &openrtb2.Device{
-				DIDMD5:   "anyDIDMD5",
-				DIDSHA1:  "anyDIDSHA1",
-				DPIDMD5:  "anyDPIDMD5",
-				DPIDSHA1: "anyDPIDSHA1",
-				MACSHA1:  "anyMACSHA1",
-				MACMD5:   "anyMACMD5",
-				IFA:      "anyIFA",
-				IP:       "1.2.3.4",
-				IPv6:     "2001:0db8:0000:0000:0000:ff00:0:0",
-				Geo:      device.Geo,
-			},
-			id:   ScrubStrategyDeviceIDNone,
-			ipv4: ScrubStrategyIPV4None,
-			ipv6: ScrubStrategyIPV6Lowest32,
+			ipv6: ScrubStrategyIPV6Subnet,
 			geo:  ScrubStrategyGeoNone,
 		},
 		{
@@ -133,7 +115,7 @@ func TestScrubDevice(t *testing.T) {
 				MACMD5:   "anyMACMD5",
 				IFA:      "anyIFA",
 				IP:       "1.2.3.4",
-				IPv6:     "2001:0db8:0000:0000:0000:ff00:0042:8329",
+				IPv6:     "2001:1db8:2233:4455:6677:ff00:0042:8329",
 				Geo: &openrtb2.Geo{
 					Lat:   123.46,
 					Lon:   678.89,
@@ -158,7 +140,7 @@ func TestScrubDevice(t *testing.T) {
 				MACMD5:   "anyMACMD5",
 				IFA:      "anyIFA",
 				IP:       "1.2.3.4",
-				IPv6:     "2001:0db8:0000:0000:0000:ff00:0042:8329",
+				IPv6:     "2001:1db8:2233:4455:6677:ff00:0042:8329",
 				Geo:      &openrtb2.Geo{},
 			},
 			id:   ScrubStrategyDeviceIDNone,
@@ -167,15 +149,16 @@ func TestScrubDevice(t *testing.T) {
 			geo:  ScrubStrategyGeoFull,
 		},
 	}
-
+	testIPMasking := getTestIPMasking()
 	for _, test := range testCases {
-		result := NewScrubber().ScrubDevice(device, test.id, test.ipv4, test.ipv6, test.geo)
+		result := NewScrubber(testIPMasking.IPv6Config, testIPMasking.IPv4Config).ScrubDevice(device, test.id, test.ipv4, test.ipv6, test.geo)
 		assert.Equal(t, test.expected, result, test.description)
 	}
 }
 
 func TestScrubDeviceNil(t *testing.T) {
-	result := NewScrubber().ScrubDevice(nil, ScrubStrategyDeviceIDNone, ScrubStrategyIPV4None, ScrubStrategyIPV6None, ScrubStrategyGeoNone)
+	testIPMasking := getTestIPMasking()
+	result := NewScrubber(testIPMasking.IPv6Config, testIPMasking.IPv4Config).ScrubDevice(nil, ScrubStrategyDeviceIDNone, ScrubStrategyIPV4None, ScrubStrategyIPV6None, ScrubStrategyGeoNone)
 	assert.Nil(t, result)
 }
 
@@ -292,14 +275,16 @@ func TestScrubUser(t *testing.T) {
 		},
 	}
 
+	testIPMasking := getTestIPMasking()
 	for _, test := range testCases {
-		result := NewScrubber().ScrubUser(user, test.scrubUser, test.scrubGeo)
+		result := NewScrubber(testIPMasking.IPv6Config, testIPMasking.IPv4Config).ScrubUser(user, test.scrubUser, test.scrubGeo)
 		assert.Equal(t, test.expected, result, test.description)
 	}
 }
 
 func TestScrubUserNil(t *testing.T) {
-	result := NewScrubber().ScrubUser(nil, ScrubStrategyUserNone, ScrubStrategyGeoNone)
+	testIPMasking := getTestIPMasking()
+	result := NewScrubber(testIPMasking.IPv6Config, testIPMasking.IPv4Config).ScrubUser(nil, ScrubStrategyUserNone, ScrubStrategyGeoNone)
 	assert.Nil(t, result)
 }
 
@@ -336,7 +321,7 @@ func TestScrubRequest(t *testing.T) {
 				},
 				Device: &openrtb2.Device{
 					IP:   "1.2.3.4",
-					IPv6: "2001:0db8:0000:0000:0000:ff00:0042:8329",
+					IPv6: "2001:1db8:2233:4455:6677:ff00:0042:8329",
 					Geo:  device.Geo,
 				},
 			},
@@ -354,7 +339,7 @@ func TestScrubRequest(t *testing.T) {
 				},
 				Device: &openrtb2.Device{
 					IP:   "1.2.3.4",
-					IPv6: "2001:0db8:0000:0000:0000:ff00:0042:8329",
+					IPv6: "2001:1db8:2233:4455:6677:ff00:0042:8329",
 					Geo:  device.Geo,
 				},
 			},
@@ -431,7 +416,7 @@ func TestScrubRequest(t *testing.T) {
 					MACSHA1:  "anyMACSHA1",
 					MACMD5:   "anyMACMD5",
 					IP:       "1.2.3.0",
-					IPv6:     "2001:0db8:0000:0000:0000:ff00:0:0",
+					IPv6:     "2001:1db8:2233:4400::",
 					Geo: &openrtb2.Geo{
 						Lat: 123.46, Lon: 678.89,
 						Metro: "some metro",
@@ -443,6 +428,7 @@ func TestScrubRequest(t *testing.T) {
 		},
 	}
 
+	testIPMasking := getTestIPMasking()
 	for _, test := range testCases {
 		t.Run(test.description, func(t *testing.T) {
 			bidRequest := &openrtb2.BidRequest{
@@ -462,127 +448,110 @@ func TestScrubRequest(t *testing.T) {
 			}
 			bidRequest.User.EIDs = []openrtb2.EID{{Source: "test"}}
 
-			result := NewScrubber().ScrubRequest(bidRequest, test.enforcement)
+			result := NewScrubber(testIPMasking.IPv6Config, testIPMasking.IPv4Config).ScrubRequest(bidRequest, test.enforcement)
 			assert.Equal(t, test.expected, result, test.description)
 		})
 	}
 }
 
-func TestScrubIPV4(t *testing.T) {
+func TestScrubIP(t *testing.T) {
 	testCases := []struct {
-		IP          string
-		cleanedIP   string
-		description string
+		IP        string
+		cleanedIP string
+		bits      int
+		maskBits  int
 	}{
 		{
-			IP:          "0.0.0.0",
-			cleanedIP:   "0.0.0.0",
-			description: "Shouldn't do anything for a 0.0.0.0 IP address",
+			IP:        "0:0:0:0:0:0:0:0",
+			cleanedIP: "::",
+			bits:      128,
+			maskBits:  56,
 		},
 		{
-			IP:          "192.127.111.134",
-			cleanedIP:   "192.127.111.0",
-			description: "Should remove the lowest 8 bits",
+			IP:        "",
+			cleanedIP: "",
+			bits:      128,
+			maskBits:  56,
 		},
 		{
-			IP:          "192.127.111.0",
-			cleanedIP:   "192.127.111.0",
-			description: "Shouldn't change anything if the lowest 8 bits are already 0",
+			IP:        "1111:2222:3333:4444:5555:6666:7777:8888",
+			cleanedIP: "1111:2222:3333:4400::",
+			bits:      128,
+			maskBits:  56,
 		},
 		{
-			IP:          "not an ip",
-			cleanedIP:   "",
-			description: "Should return an empty string for a bad IP",
+			IP:        "1111:2222:3333:4444:5555:6666:7777:8888",
+			cleanedIP: "1111:2222::",
+			bits:      128,
+			maskBits:  34,
 		},
 		{
-			IP:          "",
-			cleanedIP:   "",
-			description: "Should return an empty string for a bad IP",
-		},
-	}
-
-	for _, test := range testCases {
-		result := scrubIPV4Lowest8(test.IP)
-		assert.Equal(t, test.cleanedIP, result, test.description)
-	}
-}
-
-func TestScrubIPV6Lowest16Bits(t *testing.T) {
-	testCases := []struct {
-		IP          string
-		cleanedIP   string
-		description string
-	}{
-		{
-			IP:          "0:0:0:0",
-			cleanedIP:   "0:0:0:0",
-			description: "Shouldn't do anything for a 0:0:0:0 IP address",
+			IP:        "1111:0:3333:4444:5555:6666:7777:8888",
+			cleanedIP: "1111:0:3333:4400::",
+			bits:      128,
+			maskBits:  56,
 		},
 		{
-			IP:          "2001:0db8:0000:0000:0000:ff00:0042:8329",
-			cleanedIP:   "2001:0db8:0000:0000:0000:ff00:0042:0",
-			description: "Should remove lowest 16 bits",
+			IP:        "1111::6666:7777:8888",
+			cleanedIP: "1111::",
+			bits:      128,
+			maskBits:  56,
 		},
 		{
-			IP:          "2001:0db8:0000:0000:0000:ff00:0042:0",
-			cleanedIP:   "2001:0db8:0000:0000:0000:ff00:0042:0",
-			description: "Shouldn't do anything if the lowest 16 bits are already 0",
+			IP:        "2001:1db8:0000:0000:0000:ff00:0042:8329",
+			cleanedIP: "2001:1db8::ff00:0:0",
+			bits:      128,
+			maskBits:  96,
 		},
 		{
-			IP:          "not an ip",
-			cleanedIP:   "",
-			description: "Should return an empty string for a bad IP",
+			IP:        "2001:1db8:0000:0000:0000:ff00:0:0",
+			cleanedIP: "2001:1db8::ff00:0:0",
+			bits:      128,
+			maskBits:  96,
 		},
 		{
-			IP:          "",
-			cleanedIP:   "",
-			description: "Should return an empty string for a bad IP",
+			IP:        "2001:1db8:0000:0000:0000:ff00:0042:8329",
+			cleanedIP: "2001:1db8::ff00:42:0",
+			bits:      128,
+			maskBits:  112,
 		},
-	}
-
-	for _, test := range testCases {
-		result := scrubIPV6Lowest16Bits(test.IP)
-		assert.Equal(t, test.cleanedIP, result, test.description)
-	}
-}
-
-func TestScrubIPV6Lowest32Bits(t *testing.T) {
-	testCases := []struct {
-		IP          string
-		cleanedIP   string
-		description string
-	}{
 		{
-			IP:          "0:0:0:0",
-			cleanedIP:   "0:0:0:0",
-			description: "Shouldn't do anything for a 0:0:0:0 IP address",
+			IP:        "2001:1db8:0000:0000:0000:ff00:0042:0",
+			cleanedIP: "2001:1db8::ff00:42:0",
+			bits:      128,
+			maskBits:  112,
 		},
 		{
-			IP:          "2001:0db8:0000:0000:0000:ff00:0042:8329",
-			cleanedIP:   "2001:0db8:0000:0000:0000:ff00:0:0",
-			description: "Should remove lowest 32 bits",
+			IP:        "127.0.0.1",
+			cleanedIP: "127.0.0.0",
+			bits:      32,
+			maskBits:  24,
 		},
 		{
-			IP:          "2001:0db8:0000:0000:0000:ff00:0:0",
-			cleanedIP:   "2001:0db8:0000:0000:0000:ff00:0:0",
-			description: "Shouldn't do anything if the lowest 32 bits are already 0",
+			IP:        "0.0.0.0",
+			cleanedIP: "0.0.0.0",
+			bits:      32,
+			maskBits:  24,
 		},
-
 		{
-			IP:          "not an ip",
-			cleanedIP:   "",
-			description: "Should return an empty string for a bad IP",
+			IP:        "192.127.111.134",
+			cleanedIP: "192.127.111.0",
+			bits:      32,
+			maskBits:  24,
 		},
 		{
-			IP:          "",
-			cleanedIP:   "",
-			description: "Should return an empty string for a bad IP",
+			IP:        "192.127.111.0",
+			cleanedIP: "192.127.111.0",
+			bits:      32,
+			maskBits:  24,
 		},
 	}
-
 	for _, test := range testCases {
-		result := scrubIPV6Lowest32Bits(test.IP)
-		assert.Equal(t, test.cleanedIP, result, test.description)
+		t.Run(test.IP, func(t *testing.T) {
+			// bits: ipv6 - 128, ipv4 - 32
+			result := scrubIP(test.IP, test.maskBits, test.bits)
+			assert.Equal(t, test.cleanedIP, result)
+		})
 	}
 }
 
@@ -739,7 +708,7 @@ func getTestDevice() *openrtb2.Device {
 		MACMD5:   "anyMACMD5",
 		IFA:      "anyIFA",
 		IP:       "1.2.3.4",
-		IPv6:     "2001:0db8:0000:0000:0000:ff00:0042:8329",
+		IPv6:     "2001:1db8:2233:4455:6677:ff00:0042:8329",
 		Geo: &openrtb2.Geo{
 			Lat:   123.456,
 			Lon:   678.89,
@@ -748,5 +717,15 @@ func getTestDevice() *openrtb2.Device {
 			ZIP:   "some zip",
 		},
 	}
+}
 
+func getTestIPMasking() config.AccountPrivacy {
+	return config.AccountPrivacy{
+		IPv6Config: config.IPv6{
+			54,
+		},
+		IPv4Config: config.IPv4{
+			24,
+		},
+	}
 }
diff --git a/router/router_test.go b/router/router_test.go
index 2b4ff7fd..41c2a724 100644
--- a/router/router_test.go
+++ b/router/router_test.go
@@ -37,8 +37,9 @@ func ensureHasKey(t *testing.T, data map[string]json.RawMessage, key string) {
 }
 
 func TestNewJsonDirectoryServer(t *testing.T) {
-	alias := map[string]string{"aliastest": "appnexus"}
-	handler := NewJsonDirectoryServer("../static/bidder-params", &testValidator{}, alias)
+	defaultAlias := map[string]string{"aliastest": "appnexus"}
+	yamlAlias := map[openrtb_ext.BidderName]openrtb_ext.BidderName{openrtb_ext.BidderName("alias"): openrtb_ext.BidderName("parentAlias")}
+	handler := newJsonDirectoryServer("../static/bidder-params", &testValidator{}, defaultAlias, yamlAlias)
 	recorder := httptest.NewRecorder()
 	request, _ := http.NewRequest("GET", "/whatever", nil)
 	handler(recorder, request, nil)
@@ -59,6 +60,7 @@ func TestNewJsonDirectoryServer(t *testing.T) {
 	}
 
 	ensureHasKey(t, data, "aliastest")
+	ensureHasKey(t, data, "alias")
 }
 
 func TestCheckSupportedUserSyncEndpoints(t *testing.T) {