Repository: prebid/prebid-server
Pull request #2990 (2023-07-29 04:30:01)

IP scrubber
Added `anonymizeIpv6` function to transmitPreciseGeo activitiy to match PBS-Java implementation

Addressed issue https://github.com/prebid/prebid-server/issues/2981


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/axonix/axonix.go b/adapters/axonix/axonix.go
index 7413cec3..2289050b 100644
--- a/adapters/axonix/axonix.go
+++ b/adapters/axonix/axonix.go
@@ -5,6 +5,8 @@ import (
 	"fmt"
 	"net/http"
 	"net/url"
+	"strconv"
+	"strings"
 	"text/template"
 
 	"github.com/prebid/openrtb/v19/openrtb2"
@@ -105,6 +107,7 @@ func (a *adapter) MakeBids(request *openrtb2.BidRequest, requestData *adapters.R
 	for _, seatBid := range response.SeatBid {
 		for _, bid := range seatBid.Bid {
 			bid := bid
+			resolveMacros(&bid)
 			b := &adapters.TypedBid{
 				Bid:     &bid,
 				BidType: getMediaType(bid.ImpID, request.Imp),
@@ -129,3 +132,12 @@ func getMediaType(impId string, imps []openrtb2.Imp) openrtb_ext.BidType {
 	}
 	return openrtb_ext.BidTypeBanner
 }
+
+func resolveMacros(bid *openrtb2.Bid) {
+	if bid == nil {
+		return
+	}
+	price := strconv.FormatFloat(bid.Price, 'f', -1, 64)
+	bid.NURL = strings.Replace(bid.NURL, "${AUCTION_PRICE}", price, -1)
+	bid.AdM = strings.Replace(bid.AdM, "${AUCTION_PRICE}", price, -1)
+}
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/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..b56b7fc5 100644
--- a/endpoints/openrtb2/auction.go
+++ b/endpoints/openrtb2/auction.go
@@ -193,7 +193,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) {
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/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/openrtb_ext/bidders.go b/openrtb_ext/bidders.go
index c60b9dca..c60058eb 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,
@@ -224,17 +224,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) {
@@ -555,11 +556,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 +573,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 +622,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 +641,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/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/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-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/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/axonix/axonixtest/exemplary/simple-banner.json b/adapters/axonix/axonixtest/exemplary/simple-banner.json
index 40441a46..d1199b80 100644
--- a/adapters/axonix/axonixtest/exemplary/simple-banner.json
+++ b/adapters/axonix/axonixtest/exemplary/simple-banner.json
@@ -67,7 +67,8 @@
                 "impid": "test-imp-id",
                 "price": 0.500000,
                 "adid": "29681110",
-                "adm": "some-test-ad",
+                "adm": "some-test-ad imp_${AUCTION_PRICE} creativeview_${AUCTION_PRICE}",
+                "nurl": "nurl.link/win?p=${AUCTION_PRICE}",
                 "adomain": ["yahoo.com"],
                 "cid": "958",
                 "crid": "29681110",
@@ -90,7 +91,8 @@
           "id": "7706636740145184841",
           "impid": "test-imp-id",
           "price": 0.5,
-          "adm": "some-test-ad",
+          "adm": "some-test-ad imp_0.5 creativeview_0.5",
+          "nurl": "nurl.link/win?p=0.5",
           "adid": "29681110",
           "adomain": ["yahoo.com"],
           "cid": "958",
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/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/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/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/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) {