Repository: prebid/prebid-server
Pull request #2526 (2023-01-11 09:58:01)

Openrtb2blocking module part 1: module configuration
PRD: https://docs.google.com/document/d/1TpB_I-Oft0LpZuKvVsaLAozOe1u52tpPGkB_jZfnMXo/edit#heading=h.xm6n5vexsjib


Patch:
diff --git a/adapters/improvedigital/improvedigital.go b/adapters/improvedigital/improvedigital.go
index 27cf56ae..e7db342f 100644
--- a/adapters/improvedigital/improvedigital.go
+++ b/adapters/improvedigital/improvedigital.go
@@ -21,6 +21,7 @@ const (
 	consentProvidersSettingsInputKey = "ConsentedProvidersSettings"
 	consentProvidersSettingsOutKey   = "consented_providers_settings"
 	consentedProvidersKey            = "consented_providers"
+	publisherEndpointParam           = "{PublisherId}"
 )
 
 type ImprovedigitalAdapter struct {
@@ -35,6 +36,13 @@ type BidExt struct {
 	}
 }
 
+// ImpExtBidder represents Improved Digital bid extension with Publisher ID
+type ImpExtBidder struct {
+	Bidder struct {
+		PublisherID int `json:"publisherId"`
+	}
+}
+
 // MakeRequests makes the HTTP requests which should be made to fetch bids.
 func (a *ImprovedigitalAdapter) MakeRequests(request *openrtb2.BidRequest, reqInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) {
 	numRequests := len(request.Imp)
@@ -86,7 +94,7 @@ func (a *ImprovedigitalAdapter) makeRequest(request openrtb2.BidRequest, imp ope
 
 	return &adapters.RequestData{
 		Method:  "POST",
-		Uri:     a.endpoint,
+		Uri:     a.buildEndpointURL(imp),
 		Body:    reqJSON,
 		Headers: headers,
 	}, nil
@@ -285,3 +293,15 @@ func getImpExtWithRewardedInventory(imp openrtb2.Imp) ([]byte, error) {
 
 	return nil, nil
 }
+
+func (a *ImprovedigitalAdapter) buildEndpointURL(imp openrtb2.Imp) string {
+	publisherEndpoint := ""
+	var impBidder ImpExtBidder
+
+	err := json.Unmarshal(imp.Ext, &impBidder)
+	if err == nil && impBidder.Bidder.PublisherID != 0 {
+		publisherEndpoint = strconv.Itoa(impBidder.Bidder.PublisherID) + "/"
+	}
+
+	return strings.Replace(a.endpoint, publisherEndpointParam, publisherEndpoint, -1)
+}
diff --git a/adapters/yahoossp/yahoossp.go b/adapters/yahoossp/yahoossp.go
index 0a5e0f25..a3bbca08 100644
--- a/adapters/yahoossp/yahoossp.go
+++ b/adapters/yahoossp/yahoossp.go
@@ -169,6 +169,34 @@ func changeRequestForBidService(request *openrtb2.BidRequest, extension *openrtb
 		}
 	}
 
+	if request.Regs != nil && request.Regs.GPP != "" {
+		requestRegs := *request.Regs
+		if requestRegs.Ext == nil {
+			requestRegs.Ext = json.RawMessage("{}")
+		}
+		var regsExt map[string]json.RawMessage
+		err := json.Unmarshal(requestRegs.Ext, &regsExt)
+		if err != nil {
+			return err
+		}
+		regsExt["gpp"], err = json.Marshal(&requestRegs.GPP)
+
+		if requestRegs.GPPSID != nil {
+			regsExt["gpp_sid"], err = json.Marshal(&requestRegs.GPPSID)
+			if err != nil {
+				return err
+			}
+		}
+
+		requestRegs.Ext, err = json.Marshal(&regsExt)
+		if err != nil {
+			return err
+		}
+		requestRegs.GPP = ""
+		requestRegs.GPPSID = nil
+		request.Regs = &requestRegs
+	}
+
 	return nil
 }
 
diff --git a/config/accounts.go b/config/accounts.go
index 9f52554e..7211a988 100644
--- a/config/accounts.go
+++ b/config/accounts.go
@@ -35,6 +35,7 @@ type Account struct {
 	TruncateTargetAttribute *int                                 `mapstructure:"truncate_target_attr" json:"truncate_target_attr"`
 	AlternateBidderCodes    *openrtb_ext.ExtAlternateBidderCodes `mapstructure:"alternatebiddercodes" json:"alternatebiddercodes"`
 	Hooks                   AccountHooks                         `mapstructure:"hooks" json:"hooks"`
+	Validations             Validations                          `mapstructure:"validations" json:"validations"`
 }
 
 // CookieSync represents the account-level defaults for the cookie sync endpoint.
diff --git a/config/config.go b/config/config.go
index 38def167..6630cdbc 100644
--- a/config/config.go
+++ b/config/config.go
@@ -100,7 +100,8 @@ type Configuration struct {
 	// Refers to main.go `configFileName` constant
 	BidderInfos BidderInfos `mapstructure:"adapters"`
 	// Hooks provides a way to specify hook execution plan for specific endpoints and stages
-	Hooks Hooks `mapstructure:"hooks"`
+	Hooks       Hooks       `mapstructure:"hooks"`
+	Validations Validations `mapstructure:"validations"`
 }
 
 const MIN_COOKIE_SIZE_BYTES = 500
@@ -632,6 +633,25 @@ type TimeoutNotification struct {
 	FailOnly bool `mapstructure:"fail_only"`
 }
 
+type Validations struct {
+	BannerCreativeMaxSize string `mapstructure:"banner_creative_max_size" json:"banner_creative_max_size"`
+	SecureMarkup          string `mapstructure:"secure_markup" json:"secure_markup"`
+	MaxCreativeWidth      int64  `mapstructure:"max_creative_width" json:"max_creative_width"`
+	MaxCreativeHeight     int64  `mapstructure:"max_creative_height" json:"max_creative_height"`
+}
+
+const (
+	ValidationEnforce string = "enforce"
+	ValidationWarn    string = "warn"
+	ValidationSkip    string = "skip"
+)
+
+func (host *Validations) SetBannerCreativeMaxSize(account Validations) {
+	if len(account.BannerCreativeMaxSize) > 0 {
+		host.BannerCreativeMaxSize = account.BannerCreativeMaxSize
+	}
+}
+
 func (cfg *TimeoutNotification) validate(errs []error) []error {
 	if cfg.SamplingRate < 0.0 || cfg.SamplingRate > 1.0 {
 		errs = append(errs, fmt.Errorf("debug.timeout_notification.sampling_rate must be positive and not greater than 1.0. Got %f", cfg.SamplingRate))
@@ -822,6 +842,10 @@ func SetupViper(v *viper.Viper, filename string, bidderInfos BidderInfos) {
 	v.SetDefault("host_cookie.ttl_days", 90)
 	v.SetDefault("host_cookie.max_cookie_size_bytes", 0)
 	v.SetDefault("host_schain_node", nil)
+	v.SetDefault("validations.banner_creative_max_size", ValidationSkip)
+	v.SetDefault("validations.secure_markup", ValidationSkip)
+	v.SetDefault("validations.max_creative_size.height", 0)
+	v.SetDefault("validations.max_creative_size.width", 0)
 	v.SetDefault("http_client.max_connections_per_host", 0) // unlimited
 	v.SetDefault("http_client.max_idle_connections", 400)
 	v.SetDefault("http_client.max_idle_connections_per_host", 10)
diff --git a/endpoints/openrtb2/amp_auction.go b/endpoints/openrtb2/amp_auction.go
index 7ee9f675..5b85eb67 100644
--- a/endpoints/openrtb2/amp_auction.go
+++ b/endpoints/openrtb2/amp_auction.go
@@ -436,7 +436,7 @@ func (deps *endpointDeps) parseAmpRequest(httpRequest *http.Request) (req *openr
 	// At this point, we should have a valid request that definitely has Targeting and Cache turned on
 	hasStoredResponses := len(storedAuctionResponses) > 0
 
-	e = deps.validateRequest(req, true, hasStoredResponses, storedBidResponses)
+	e = deps.validateRequest(req, true, hasStoredResponses, storedBidResponses, false)
 	errs = append(errs, e...)
 	return
 }
diff --git a/endpoints/openrtb2/auction.go b/endpoints/openrtb2/auction.go
index 6b8e875a..44005a7d 100644
--- a/endpoints/openrtb2/auction.go
+++ b/endpoints/openrtb2/auction.go
@@ -450,7 +450,7 @@ func (deps *endpointDeps) parseRequest(httpRequest *http.Request, labels *metric
 	if len(storedAuctionResponses) > 0 {
 		hasStoredResponses = true
 	}
-	errL := deps.validateRequest(req, false, hasStoredResponses, storedBidResponses)
+	errL := deps.validateRequest(req, false, hasStoredResponses, storedBidResponses, hasStoredBidRequest)
 	if len(errL) > 0 {
 		errs = append(errs, errL...)
 	}
@@ -635,7 +635,7 @@ func mergeBidderParamsImpExtPrebid(impExt *openrtb_ext.ImpExt, reqExtParams map[
 	return nil
 }
 
-func (deps *endpointDeps) validateRequest(req *openrtb_ext.RequestWrapper, isAmp bool, hasStoredResponses bool, storedBidResp stored_responses.ImpBidderStoredResp) []error {
+func (deps *endpointDeps) validateRequest(req *openrtb_ext.RequestWrapper, isAmp bool, hasStoredResponses bool, storedBidResp stored_responses.ImpBidderStoredResp, hasStoredBidRequest bool) []error {
 	errL := []error{}
 	if req.ID == "" {
 		return []error{errors.New("request missing required field: \"id\"")}
@@ -657,7 +657,7 @@ func (deps *endpointDeps) validateRequest(req *openrtb_ext.RequestWrapper, isAmp
 	// If automatically filling source TID is enabled then validate that
 	// source.TID exists and If it doesn't, fill it with a randomly generated UUID
 	if deps.cfg.AutoGenSourceTID {
-		if err := validateAndFillSourceTID(req.BidRequest); err != nil {
+		if err := validateAndFillSourceTID(req, deps.cfg.GenerateRequestID, hasStoredBidRequest, isAmp); err != nil {
 			return []error{err}
 		}
 	}
@@ -808,17 +808,31 @@ func mapSChains(req *openrtb_ext.RequestWrapper) error {
 	return nil
 }
 
-func validateAndFillSourceTID(req *openrtb2.BidRequest) error {
+func validateAndFillSourceTID(req *openrtb_ext.RequestWrapper, generateRequestID bool, hasStoredBidRequest bool, isAmp bool) error {
 	if req.Source == nil {
 		req.Source = &openrtb2.Source{}
 	}
-	if req.Source.TID == "" {
-		if rawUUID, err := uuid.NewV4(); err == nil {
-			req.Source.TID = rawUUID.String()
-		} else {
-			return errors.New("req.Source.TID missing in the req and error creating a random UID")
+
+	if req.Source.TID == "" || req.Source.TID == "{{UUID}}" || (generateRequestID && (isAmp || hasStoredBidRequest)) {
+		rawUUID, err := uuid.NewV4()
+		if err != nil {
+			return errors.New("error creating a random UUID for source.tid")
 		}
+		req.Source.TID = rawUUID.String()
 	}
+
+	for _, impWrapper := range req.GetImp() {
+		ie, _ := impWrapper.GetImpExt()
+		if ie.GetTid() == "" || ie.GetTid() == "{{UUID}}" || (generateRequestID && (isAmp || hasStoredBidRequest)) {
+			rawUUID, err := uuid.NewV4()
+			if err != nil {
+				return errors.New("imp.ext.tid missing in the imp and error creating a random UID")
+			}
+			ie.SetTid(rawUUID.String())
+			impWrapper.RebuildImp()
+		}
+	}
+
 	return nil
 }
 
diff --git a/endpoints/openrtb2/video_auction.go b/endpoints/openrtb2/video_auction.go
index 5c71bc5e..ea1cd46b 100644
--- a/endpoints/openrtb2/video_auction.go
+++ b/endpoints/openrtb2/video_auction.go
@@ -253,7 +253,7 @@ func (deps *endpointDeps) VideoAuctionEndpoint(w http.ResponseWriter, r *http.Re
 	// Populate any "missing" OpenRTB fields with info from other sources, (e.g. HTTP request headers).
 	deps.setFieldsImplicitly(r, bidReqWrapper)
 
-	errL = deps.validateRequest(bidReqWrapper, false, false, nil)
+	errL = deps.validateRequest(bidReqWrapper, false, false, nil, false)
 	if errortypes.ContainsFatalError(errL) {
 		handleError(&labels, w, errL, &vo, &debugLog)
 		return
diff --git a/exchange/exchange.go b/exchange/exchange.go
index d7737a42..9686fc48 100644
--- a/exchange/exchange.go
+++ b/exchange/exchange.go
@@ -55,23 +55,24 @@ type IdFetcher interface {
 }
 
 type exchange struct {
-	adapterMap        map[openrtb_ext.BidderName]AdaptedBidder
-	bidderInfo        config.BidderInfos
-	bidderToSyncerKey map[string]string
-	me                metrics.MetricsEngine
-	cache             prebid_cache_client.Client
-	cacheTime         time.Duration
-	gdprPermsBuilder  gdpr.PermissionsBuilder
-	tcf2ConfigBuilder gdpr.TCF2ConfigBuilder
-	currencyConverter *currency.RateConverter
-	externalURL       string
-	gdprDefaultValue  gdpr.Signal
-	privacyConfig     config.Privacy
-	categoriesFetcher stored_requests.CategoryFetcher
-	bidIDGenerator    BidIDGenerator
-	hostSChainNode    *openrtb2.SupplyChainNode
-	adsCertSigner     adscert.Signer
-	server            config.Server
+	adapterMap               map[openrtb_ext.BidderName]AdaptedBidder
+	bidderInfo               config.BidderInfos
+	bidderToSyncerKey        map[string]string
+	me                       metrics.MetricsEngine
+	cache                    prebid_cache_client.Client
+	cacheTime                time.Duration
+	gdprPermsBuilder         gdpr.PermissionsBuilder
+	tcf2ConfigBuilder        gdpr.TCF2ConfigBuilder
+	currencyConverter        *currency.RateConverter
+	externalURL              string
+	gdprDefaultValue         gdpr.Signal
+	privacyConfig            config.Privacy
+	categoriesFetcher        stored_requests.CategoryFetcher
+	bidIDGenerator           BidIDGenerator
+	hostSChainNode           *openrtb2.SupplyChainNode
+	adsCertSigner            adscert.Signer
+	server                   config.Server
+	bidValidationEnforcement config.Validations
 }
 
 // Container to pass out response ext data from the GetAllBids goroutines back into the main thread
@@ -147,10 +148,11 @@ func NewExchange(adapters map[openrtb_ext.BidderName]AdaptedBidder, cache prebid
 			GDPR: cfg.GDPR,
 			LMT:  cfg.LMT,
 		},
-		bidIDGenerator: &bidIDGenerator{cfg.GenerateBidID},
-		hostSChainNode: cfg.HostSChainNode,
-		adsCertSigner:  adsCertSigner,
-		server:         config.Server{ExternalUrl: cfg.ExternalURL, GvlID: cfg.GDPR.HostVendorID, DataCenter: cfg.DataCenter},
+		bidIDGenerator:           &bidIDGenerator{cfg.GenerateBidID},
+		hostSChainNode:           cfg.HostSChainNode,
+		adsCertSigner:            adsCertSigner,
+		server:                   config.Server{ExternalUrl: cfg.ExternalURL, GvlID: cfg.GDPR.HostVendorID, DataCenter: cfg.DataCenter},
+		bidValidationEnforcement: cfg.Validations,
 	}
 }
 
@@ -404,9 +406,10 @@ func (e *exchange) HoldAuction(ctx context.Context, r AuctionRequest, debugLog *
 		bidResponseExt.Warnings[openrtb_ext.BidderReservedGeneral] = append(bidResponseExt.Warnings[openrtb_ext.BidderReservedGeneral], generalWarning)
 	}
 
-	// Build the response
-	bidResponse, err := e.buildBidResponse(ctx, liveAdapters, adapterBids, r.BidRequestWrapper.BidRequest, adapterExtra, auc, bidResponseExt, cacheInstructions.returnCreative, r.ImpExtInfoMap, errs)
+	e.bidValidationEnforcement.SetBannerCreativeMaxSize(r.Account.Validations)
 
+	// Build the response
+	bidResponse, err := e.buildBidResponse(ctx, liveAdapters, adapterBids, r.BidRequestWrapper.BidRequest, adapterExtra, auc, bidResponseExt, cacheInstructions.returnCreative, r.ImpExtInfoMap, r.PubID, errs)
 	return bidResponse, err
 }
 
@@ -706,7 +709,7 @@ func errsToBidderWarnings(errs []error) []openrtb_ext.ExtBidderMessage {
 }
 
 // This piece takes all the bids supplied by the adapters and crafts an openRTB response to send back to the requester
-func (e *exchange) buildBidResponse(ctx context.Context, liveAdapters []openrtb_ext.BidderName, adapterSeatBids map[openrtb_ext.BidderName]*entities.PbsOrtbSeatBid, bidRequest *openrtb2.BidRequest, adapterExtra map[openrtb_ext.BidderName]*seatResponseExtra, auc *auction, bidResponseExt *openrtb_ext.ExtBidResponse, returnCreative bool, impExtInfoMap map[string]ImpExtInfo, errList []error) (*openrtb2.BidResponse, error) {
+func (e *exchange) buildBidResponse(ctx context.Context, liveAdapters []openrtb_ext.BidderName, adapterSeatBids map[openrtb_ext.BidderName]*entities.PbsOrtbSeatBid, bidRequest *openrtb2.BidRequest, adapterExtra map[openrtb_ext.BidderName]*seatResponseExtra, auc *auction, bidResponseExt *openrtb_ext.ExtBidResponse, returnCreative bool, impExtInfoMap map[string]ImpExtInfo, pubID string, errList []error) (*openrtb2.BidResponse, error) {
 	bidResponse := new(openrtb2.BidResponse)
 	var err error
 
@@ -722,7 +725,7 @@ func (e *exchange) buildBidResponse(ctx context.Context, liveAdapters []openrtb_
 	for a, adapterSeatBids := range adapterSeatBids {
 		//while processing every single bib, do we need to handle categories here?
 		if adapterSeatBids != nil && len(adapterSeatBids.Bids) > 0 {
-			sb := e.makeSeatBid(adapterSeatBids, a, adapterExtra, auc, returnCreative, impExtInfoMap)
+			sb := e.makeSeatBid(adapterSeatBids, a, adapterExtra, auc, returnCreative, impExtInfoMap, bidResponseExt, pubID)
 			seatBids = append(seatBids, *sb)
 			bidResponse.Cur = adapterSeatBids.Currency
 		}
@@ -1022,14 +1025,14 @@ func (e *exchange) makeExtBidResponse(adapterBids map[openrtb_ext.BidderName]*en
 
 // Return an openrtb seatBid for a bidder
 // BuildBidResponse is responsible for ensuring nil bid seatbids are not included
-func (e *exchange) makeSeatBid(adapterBid *entities.PbsOrtbSeatBid, adapter openrtb_ext.BidderName, adapterExtra map[openrtb_ext.BidderName]*seatResponseExtra, auc *auction, returnCreative bool, impExtInfoMap map[string]ImpExtInfo) *openrtb2.SeatBid {
+func (e *exchange) makeSeatBid(adapterBid *entities.PbsOrtbSeatBid, adapter openrtb_ext.BidderName, adapterExtra map[openrtb_ext.BidderName]*seatResponseExtra, auc *auction, returnCreative bool, impExtInfoMap map[string]ImpExtInfo, bidResponseExt *openrtb_ext.ExtBidResponse, pubID string) *openrtb2.SeatBid {
 	seatBid := &openrtb2.SeatBid{
 		Seat:  adapter.String(),
 		Group: 0, // Prebid cannot support roadblocking
 	}
 
 	var errList []error
-	seatBid.Bid, errList = e.makeBid(adapterBid.Bids, auc, returnCreative, impExtInfoMap)
+	seatBid.Bid, errList = e.makeBid(adapterBid.Bids, auc, returnCreative, impExtInfoMap, bidResponseExt, adapter, pubID)
 	if len(errList) > 0 {
 		adapterExtra[adapter].Errors = append(adapterExtra[adapter].Errors, errsToBidderErrors(errList)...)
 	}
@@ -1037,11 +1040,28 @@ func (e *exchange) makeSeatBid(adapterBid *entities.PbsOrtbSeatBid, adapter open
 	return seatBid
 }
 
-func (e *exchange) makeBid(bids []*entities.PbsOrtbBid, auc *auction, returnCreative bool, impExtInfoMap map[string]ImpExtInfo) ([]openrtb2.Bid, []error) {
+func (e *exchange) makeBid(bids []*entities.PbsOrtbBid, auc *auction, returnCreative bool, impExtInfoMap map[string]ImpExtInfo, bidResponseExt *openrtb_ext.ExtBidResponse, adapter openrtb_ext.BidderName, pubID string) ([]openrtb2.Bid, []error) {
 	result := make([]openrtb2.Bid, 0, len(bids))
 	errs := make([]error, 0, 1)
 
 	for _, bid := range bids {
+		if e.bidValidationEnforcement.BannerCreativeMaxSize == config.ValidationEnforce && bid.BidType == openrtb_ext.BidTypeBanner {
+			if !e.validateBannerCreativeSize(bid, bidResponseExt, adapter, pubID, e.bidValidationEnforcement.BannerCreativeMaxSize) {
+				continue // Don't add bid to result
+			}
+		} else if e.bidValidationEnforcement.BannerCreativeMaxSize == config.ValidationWarn && bid.BidType == openrtb_ext.BidTypeBanner {
+			e.validateBannerCreativeSize(bid, bidResponseExt, adapter, pubID, e.bidValidationEnforcement.BannerCreativeMaxSize)
+		}
+		if _, ok := impExtInfoMap[bid.Bid.ImpID]; ok {
+			if e.bidValidationEnforcement.SecureMarkup == config.ValidationEnforce && (bid.BidType == openrtb_ext.BidTypeBanner || bid.BidType == openrtb_ext.BidTypeVideo) {
+				if !e.validateBidAdM(bid, bidResponseExt, adapter, pubID, e.bidValidationEnforcement.SecureMarkup) {
+					continue // Don't add bid to result
+				}
+			} else if e.bidValidationEnforcement.SecureMarkup == config.ValidationWarn && (bid.BidType == openrtb_ext.BidTypeBanner || bid.BidType == openrtb_ext.BidTypeVideo) {
+				e.validateBidAdM(bid, bidResponseExt, adapter, pubID, e.bidValidationEnforcement.SecureMarkup)
+			}
+
+		}
 		bidExtPrebid := &openrtb_ext.ExtBidPrebid{
 			DealPriority:      bid.DealPriority,
 			DealTierSatisfied: bid.DealTierSatisfied,
@@ -1255,3 +1275,60 @@ func isAdsCertEnabled(experiment *openrtb_ext.Experiment, info config.BidderInfo
 	bidderAdsCertEnabled := info.Experiment.AdsCert.Enabled
 	return requestAdsCertEnabled && bidderAdsCertEnabled
 }
+
+func (e exchange) validateBannerCreativeSize(bid *entities.PbsOrtbBid, bidResponseExt *openrtb_ext.ExtBidResponse, adapter openrtb_ext.BidderName, pubID string, validationType string) bool {
+	if bid.Bid.W > e.bidValidationEnforcement.MaxCreativeWidth || bid.Bid.H > e.bidValidationEnforcement.MaxCreativeHeight {
+		// Add error to debug array
+		errorMessage := setErrorMessageCreativeSize(validationType)
+		bidCreativeMaxSizeError := openrtb_ext.ExtBidderMessage{
+			Code:    errortypes.BadServerResponseErrorCode,
+			Message: errorMessage,
+		}
+		bidResponseExt.Errors[adapter] = append(bidResponseExt.Errors[adapter], bidCreativeMaxSizeError)
+
+		// Log Metrics
+		e.me.RecordBidValidationCreativeSizeError(adapter, pubID)
+
+		return false
+	}
+	return true
+}
+
+func (e exchange) validateBidAdM(bid *entities.PbsOrtbBid, bidResponseExt *openrtb_ext.ExtBidResponse, adapter openrtb_ext.BidderName, pubID string, validationType string) bool {
+	invalidAdM := []string{"http:", "http%3A"}
+	requiredAdM := []string{"https:", "https%3A"}
+
+	if (strings.Contains(bid.Bid.AdM, invalidAdM[0]) || strings.Contains(bid.Bid.AdM, invalidAdM[1])) && (!strings.Contains(bid.Bid.AdM, requiredAdM[0]) && !strings.Contains(bid.Bid.AdM, requiredAdM[1])) {
+		// Add error to debug array
+		errorMessage := setErrorMessageSecureMarkup(validationType)
+		bidSecureMarkupError := openrtb_ext.ExtBidderMessage{
+			Code:    errortypes.BadServerResponseErrorCode,
+			Message: errorMessage,
+		}
+		bidResponseExt.Errors[adapter] = append(bidResponseExt.Errors[adapter], bidSecureMarkupError)
+
+		// Log Metrics
+		e.me.RecordBidValidationSecureMarkupError(adapter, pubID)
+
+		return false
+	}
+	return true
+}
+
+func setErrorMessageCreativeSize(validationType string) string {
+	if validationType == config.ValidationEnforce {
+		return "bidResponse rejected: size WxH"
+	} else if validationType == config.ValidationWarn {
+		return "bidResponse creative size warning: size WxH larger than AdUnit sizes"
+	}
+	return ""
+}
+
+func setErrorMessageSecureMarkup(validationType string) string {
+	if validationType == config.ValidationEnforce {
+		return "bidResponse rejected: insecure creative in secure context"
+	} else if validationType == config.ValidationWarn {
+		return "bidResponse secure markup warning: insecure creative in secure contexts"
+	}
+	return ""
+}
diff --git a/hooks/hookexecution/errors.go b/hooks/hookexecution/errors.go
index 62c6fb71..b1cf912c 100644
--- a/hooks/hookexecution/errors.go
+++ b/hooks/hookexecution/errors.go
@@ -13,6 +13,10 @@ func (e TimeoutError) Error() string {
 	return "Hook execution timeout"
 }
 
+func NewFailure(format string, a ...any) FailureError {
+	return FailureError{Message: fmt.Sprintf(format, a...)}
+}
+
 // FailureError indicates expected error occurred during hook execution on the module-side.
 // A moduleFailed metric will be sent in such case.
 type FailureError struct {
diff --git a/hooks/hookexecution/execution.go b/hooks/hookexecution/execution.go
index 52790501..1d9b4a52 100644
--- a/hooks/hookexecution/execution.go
+++ b/hooks/hookexecution/execution.go
@@ -269,7 +269,7 @@ func handleHookMutations[P any](
 	metricEngine metrics.MetricsEngine,
 	labels metrics.ModuleLabels,
 ) P {
-	if hr.Result.ChangeSet == nil || len(hr.Result.ChangeSet.Mutations()) == 0 {
+	if len(hr.Result.ChangeSet.Mutations()) == 0 {
 		metricEngine.RecordModuleSuccessNooped(labels)
 		hookOutcome.Action = ActionNone
 		return payload
diff --git a/hooks/hookstage/invocation.go b/hooks/hookstage/invocation.go
index afea4a2f..7f465382 100644
--- a/hooks/hookstage/invocation.go
+++ b/hooks/hookstage/invocation.go
@@ -8,10 +8,10 @@ import (
 
 // HookResult represents the result of execution the concrete hook instance.
 type HookResult[T any] struct {
-	Reject        bool          // true value indicates rejection of the program execution at the specific stage
-	NbrCode       int           // hook must provide NbrCode if the field Reject set to true
-	Message       string        // holds arbitrary message added by hook
-	ChangeSet     *ChangeSet[T] // set of changes the module wants to apply to hook payload in case of successful execution
+	Reject        bool         // true value indicates rejection of the program execution at the specific stage
+	NbrCode       int          // hook must provide NbrCode if the field Reject set to true
+	Message       string       // holds arbitrary message added by hook
+	ChangeSet     ChangeSet[T] // set of changes the module wants to apply to hook payload in case of successful execution
 	Errors        []string
 	Warnings      []string
 	DebugMessages []string
diff --git a/metrics/config/metrics.go b/metrics/config/metrics.go
index 4f232445..f0ac46fa 100644
--- a/metrics/config/metrics.go
+++ b/metrics/config/metrics.go
@@ -279,6 +279,30 @@ func (me *MultiMetricsEngine) RecordAdsCertSignTime(adsCertSignTime time.Duratio
 	}
 }
 
+func (me *MultiMetricsEngine) RecordBidValidationCreativeSizeError(adapter openrtb_ext.BidderName, account string) {
+	for _, thisME := range *me {
+		thisME.RecordBidValidationCreativeSizeError(adapter, account)
+	}
+}
+
+func (me *MultiMetricsEngine) RecordBidValidationCreativeSizeWarn(adapter openrtb_ext.BidderName, account string) {
+	for _, thisME := range *me {
+		thisME.RecordBidValidationCreativeSizeWarn(adapter, account)
+	}
+}
+
+func (me *MultiMetricsEngine) RecordBidValidationSecureMarkupError(adapter openrtb_ext.BidderName, account string) {
+	for _, thisME := range *me {
+		thisME.RecordBidValidationSecureMarkupError(adapter, account)
+	}
+}
+
+func (me *MultiMetricsEngine) RecordBidValidationSecureMarkupWarn(adapter openrtb_ext.BidderName, account string) {
+	for _, thisME := range *me {
+		thisME.RecordBidValidationSecureMarkupWarn(adapter, account)
+	}
+}
+
 func (me *MultiMetricsEngine) RecordModuleCalled(labels metrics.ModuleLabels, duration time.Duration) {
 	for _, thisME := range *me {
 		thisME.RecordModuleCalled(labels, duration)
@@ -448,6 +472,18 @@ func (me *NilMetricsEngine) RecordAdsCertSignTime(adsCertSignTime time.Duration)
 
 }
 
+func (me *NilMetricsEngine) RecordBidValidationCreativeSizeError(adapter openrtb_ext.BidderName, account string) {
+}
+
+func (me *NilMetricsEngine) RecordBidValidationCreativeSizeWarn(adapter openrtb_ext.BidderName, account string) {
+}
+
+func (me *NilMetricsEngine) RecordBidValidationSecureMarkupError(adapter openrtb_ext.BidderName, account string) {
+}
+
+func (me *NilMetricsEngine) RecordBidValidationSecureMarkupWarn(adapter openrtb_ext.BidderName, account string) {
+}
+
 func (me *NilMetricsEngine) RecordModuleCalled(labels metrics.ModuleLabels, duration time.Duration) {
 }
 
diff --git a/metrics/go_metrics.go b/metrics/go_metrics.go
index 048eb8a7..1897a1ef 100644
--- a/metrics/go_metrics.go
+++ b/metrics/go_metrics.go
@@ -96,6 +96,12 @@ type AdapterMetrics struct {
 	ConnReused         metrics.Counter
 	ConnWaitTime       metrics.Timer
 	GDPRRequestBlocked metrics.Meter
+
+	BidValidationCreativeSizeErrorMeter metrics.Meter
+	BidValidationCreativeSizeWarnMeter  metrics.Meter
+
+	BidValidationSecureMarkupErrorMeter metrics.Meter
+	BidValidationSecureMarkupWarnMeter  metrics.Meter
 }
 
 type MarkupDeliveryMetrics struct {
@@ -112,6 +118,12 @@ type accountMetrics struct {
 	adapterMetrics       map[openrtb_ext.BidderName]*AdapterMetrics
 	moduleMetrics        map[string]*ModuleMetrics
 	storedResponsesMeter metrics.Meter
+
+	bidValidationCreativeSizeMeter     metrics.Meter
+	bidValidationCreativeSizeWarnMeter metrics.Meter
+
+	bidValidationSecureMarkupMeter     metrics.Meter
+	bidValidationSecureMarkupWarnMeter metrics.Meter
 }
 
 type ModuleMetrics struct {
@@ -442,6 +454,12 @@ func registerAdapterMetrics(registry metrics.Registry, adapterOrAccount string,
 	}
 	am.PanicMeter = metrics.GetOrRegisterMeter(fmt.Sprintf("%[1]s.%[2]s.requests.panic", adapterOrAccount, exchange), registry)
 	am.GDPRRequestBlocked = metrics.GetOrRegisterMeter(fmt.Sprintf("%[1]s.%[2]s.gdpr_request_blocked", adapterOrAccount, exchange), registry)
+
+	am.BidValidationCreativeSizeErrorMeter = metrics.GetOrRegisterMeter(fmt.Sprintf("%[1]s.%[2]s.response.validation.size.err", adapterOrAccount, exchange), registry)
+	am.BidValidationCreativeSizeWarnMeter = metrics.GetOrRegisterMeter(fmt.Sprintf("%[1]s.%[2]s.response.validation.size.warn", adapterOrAccount, exchange), registry)
+
+	am.BidValidationSecureMarkupErrorMeter = metrics.GetOrRegisterMeter(fmt.Sprintf("%[1]s.%[2]s.response.validation.secure.err", adapterOrAccount, exchange), registry)
+	am.BidValidationSecureMarkupWarnMeter = metrics.GetOrRegisterMeter(fmt.Sprintf("%[1]s.%[2]s.response.validation.secure.warn", adapterOrAccount, exchange), registry)
 }
 
 func registerModuleMetrics(registry metrics.Registry, module string, stages []string, mm map[string]*ModuleMetrics) {
@@ -512,6 +530,12 @@ func (me *Metrics) getAccountMetrics(id string) *accountMetrics {
 			registerAdapterMetrics(me.MetricsRegistry, fmt.Sprintf("account.%s", id), string(a), am.adapterMetrics[a])
 		}
 	}
+	am.bidValidationCreativeSizeMeter = metrics.GetOrRegisterMeter(fmt.Sprintf("account.%s.response.validation.size.err", id), me.MetricsRegistry)
+	am.bidValidationCreativeSizeWarnMeter = metrics.GetOrRegisterMeter(fmt.Sprintf("account.%s.response.validation.size.warn", id), me.MetricsRegistry)
+
+	am.bidValidationSecureMarkupMeter = metrics.GetOrRegisterMeter(fmt.Sprintf("account.%s.response.validation.secure.err", id), me.MetricsRegistry)
+	am.bidValidationSecureMarkupWarnMeter = metrics.GetOrRegisterMeter(fmt.Sprintf("account.%s.response.validation.secure.warn", id), me.MetricsRegistry)
+
 	if !me.MetricsDisabled.AccountModulesMetrics {
 		for _, mod := range me.modules {
 			am.moduleMetrics[mod] = makeBlankModuleMetrics()
@@ -874,6 +898,62 @@ func (me *Metrics) RecordAdsCertSignTime(adsCertSignTime time.Duration) {
 	me.adsCertSignTimer.Update(adsCertSignTime)
 }
 
+func (me *Metrics) RecordBidValidationCreativeSizeError(adapter openrtb_ext.BidderName, pubID string) {
+	am, ok := me.AdapterMetrics[adapter]
+	if !ok {
+		glog.Errorf("Trying to run adapter metrics on %s: adapter metrics not found", string(adapter))
+		return
+	}
+	am.BidValidationCreativeSizeErrorMeter.Mark(1)
+
+	aam := me.getAccountMetrics(pubID)
+	if !me.MetricsDisabled.AccountAdapterDetails {
+		aam.bidValidationCreativeSizeMeter.Mark(1)
+	}
+}
+
+func (me *Metrics) RecordBidValidationCreativeSizeWarn(adapter openrtb_ext.BidderName, pubID string) {
+	am, ok := me.AdapterMetrics[adapter]
+	if !ok {
+		glog.Errorf("Trying to run adapter metrics on %s: adapter metrics not found", string(adapter))
+		return
+	}
+	am.BidValidationCreativeSizeWarnMeter.Mark(1)
+
+	aam := me.getAccountMetrics(pubID)
+	if !me.MetricsDisabled.AccountAdapterDetails {
+		aam.bidValidationCreativeSizeWarnMeter.Mark(1)
+	}
+}
+
+func (me *Metrics) RecordBidValidationSecureMarkupError(adapter openrtb_ext.BidderName, pubID string) {
+	am, ok := me.AdapterMetrics[adapter]
+	if !ok {
+		glog.Errorf("Trying to run adapter metrics on %s: adapter metrics not found", string(adapter))
+		return
+	}
+	am.BidValidationSecureMarkupErrorMeter.Mark(1)
+
+	aam := me.getAccountMetrics(pubID)
+	if !me.MetricsDisabled.AccountAdapterDetails {
+		aam.bidValidationSecureMarkupMeter.Mark(1)
+	}
+}
+
+func (me *Metrics) RecordBidValidationSecureMarkupWarn(adapter openrtb_ext.BidderName, pubID string) {
+	am, ok := me.AdapterMetrics[adapter]
+	if !ok {
+		glog.Errorf("Trying to run adapter metrics on %s: adapter metrics not found", string(adapter))
+		return
+	}
+	am.BidValidationSecureMarkupWarnMeter.Mark(1)
+
+	aam := me.getAccountMetrics(pubID)
+	if !me.MetricsDisabled.AccountAdapterDetails {
+		aam.bidValidationSecureMarkupWarnMeter.Mark(1)
+	}
+}
+
 func (me *Metrics) RecordModuleCalled(labels ModuleLabels, duration time.Duration) {
 	mm, err := me.getModuleMetric(labels)
 	if err != nil {
diff --git a/metrics/metrics.go b/metrics/metrics.go
index 4acee77f..d81026d5 100644
--- a/metrics/metrics.go
+++ b/metrics/metrics.go
@@ -435,6 +435,10 @@ type MetricsEngine interface {
 	RecordStoredResponse(pubId string)
 	RecordAdsCertReq(success bool)
 	RecordAdsCertSignTime(adsCertSignTime time.Duration)
+	RecordBidValidationCreativeSizeError(adapter openrtb_ext.BidderName, account string)
+	RecordBidValidationCreativeSizeWarn(adapter openrtb_ext.BidderName, account string)
+	RecordBidValidationSecureMarkupError(adapter openrtb_ext.BidderName, account string)
+	RecordBidValidationSecureMarkupWarn(adapter openrtb_ext.BidderName, account string)
 	RecordModuleCalled(labels ModuleLabels, duration time.Duration)
 	RecordModuleFailed(labels ModuleLabels)
 	RecordModuleSuccessNooped(labels ModuleLabels)
diff --git a/metrics/metrics_mock.go b/metrics/metrics_mock.go
index 7bdd0562..5184f33c 100644
--- a/metrics/metrics_mock.go
+++ b/metrics/metrics_mock.go
@@ -163,6 +163,22 @@ func (me *MetricsEngineMock) RecordAdsCertSignTime(adsCertSignTime time.Duration
 	me.Called(adsCertSignTime)
 }
 
+func (me *MetricsEngineMock) RecordBidValidationCreativeSizeError(adapter openrtb_ext.BidderName, account string) {
+	me.Called(adapter, account)
+}
+
+func (me *MetricsEngineMock) RecordBidValidationCreativeSizeWarn(adapter openrtb_ext.BidderName, account string) {
+	me.Called(adapter, account)
+}
+
+func (me *MetricsEngineMock) RecordBidValidationSecureMarkupError(adapter openrtb_ext.BidderName, account string) {
+	me.Called(adapter, account)
+}
+
+func (me *MetricsEngineMock) RecordBidValidationSecureMarkupWarn(adapter openrtb_ext.BidderName, account string) {
+	me.Called(adapter, account)
+}
+
 func (me *MetricsEngineMock) RecordModuleCalled(labels ModuleLabels, duration time.Duration) {
 	me.Called(labels, duration)
 }
diff --git a/metrics/prometheus/preload.go b/metrics/prometheus/preload.go
index a61f26c3..61ae568d 100644
--- a/metrics/prometheus/preload.go
+++ b/metrics/prometheus/preload.go
@@ -135,6 +135,26 @@ func preloadLabelValues(m *Metrics, syncerKeys []string, moduleStageNames map[st
 		adapterLabel: adapterValues,
 	})
 
+	preloadLabelValuesForCounter(m.adapterBidResponseSecureMarkupError, map[string][]string{
+		adapterLabel: adapterValues,
+		successLabel: boolValues,
+	})
+
+	preloadLabelValuesForCounter(m.adapterBidResponseSecureMarkupWarn, map[string][]string{
+		adapterLabel: adapterValues,
+		successLabel: boolValues,
+	})
+
+	preloadLabelValuesForCounter(m.adapterBidResponseValidationSizeError, map[string][]string{
+		adapterLabel: adapterValues,
+		successLabel: boolValues,
+	})
+
+	preloadLabelValuesForCounter(m.adapterBidResponseValidationSizeWarn, map[string][]string{
+		adapterLabel: adapterValues,
+		successLabel: boolValues,
+	})
+
 	preloadLabelValuesForHistogram(m.adapterPrices, map[string][]string{
 		adapterLabel: adapterValues,
 	})
diff --git a/metrics/prometheus/prometheus.go b/metrics/prometheus/prometheus.go
index ca5166cd..fad0b873 100644
--- a/metrics/prometheus/prometheus.go
+++ b/metrics/prometheus/prometheus.go
@@ -58,25 +58,34 @@ type Metrics struct {
 	adsCertSignTimer             prometheus.Histogram
 
 	// Adapter Metrics
-	adapterBids                *prometheus.CounterVec
-	adapterErrors              *prometheus.CounterVec
-	adapterPanics              *prometheus.CounterVec
-	adapterPrices              *prometheus.HistogramVec
-	adapterRequests            *prometheus.CounterVec
-	adapterRequestsTimer       *prometheus.HistogramVec
-	adapterReusedConnections   *prometheus.CounterVec
-	adapterCreatedConnections  *prometheus.CounterVec
-	adapterConnectionWaitTime  *prometheus.HistogramVec
-	adapterGDPRBlockedRequests *prometheus.CounterVec
+	adapterBids                           *prometheus.CounterVec
+	adapterErrors                         *prometheus.CounterVec
+	adapterPanics                         *prometheus.CounterVec
+	adapterPrices                         *prometheus.HistogramVec
+	adapterRequests                       *prometheus.CounterVec
+	adapterRequestsTimer                  *prometheus.HistogramVec
+	adapterReusedConnections              *prometheus.CounterVec
+	adapterCreatedConnections             *prometheus.CounterVec
+	adapterConnectionWaitTime             *prometheus.HistogramVec
+	adapterGDPRBlockedRequests            *prometheus.CounterVec
+	adapterBidResponseValidationSizeError *prometheus.CounterVec
+	adapterBidResponseValidationSizeWarn  *prometheus.CounterVec
+	adapterBidResponseSecureMarkupError   *prometheus.CounterVec
+	adapterBidResponseSecureMarkupWarn    *prometheus.CounterVec
 
 	// Syncer Metrics
 	syncerRequests *prometheus.CounterVec
 	syncerSets     *prometheus.CounterVec
 
 	// Account Metrics
-	accountRequests        *prometheus.CounterVec
-	accountDebugRequests   *prometheus.CounterVec
-	accountStoredResponses *prometheus.CounterVec
+	accountRequests                       *prometheus.CounterVec
+	accountDebugRequests                  *prometheus.CounterVec
+	accountStoredResponses                *prometheus.CounterVec
+	accountBidResponseValidationSizeError *prometheus.CounterVec
+	accountBidResponseValidationSizeWarn  *prometheus.CounterVec
+
+	accountBidResponseSecureMarkupError *prometheus.CounterVec
+	accountBidResponseSecureMarkupWarn  *prometheus.CounterVec
 
 	// Module Metrics as a map where the key is the module name
 	moduleDuration        map[string]*prometheus.HistogramVec
@@ -387,6 +396,26 @@ func NewMetrics(cfg config.PrometheusMetrics, disabledMetrics config.DisabledMet
 			standardTimeBuckets)
 	}
 
+	metrics.adapterBidResponseValidationSizeError = newCounter(cfg, reg,
+		"adapter_response_validation_size_err",
+		"Count that tracks number of bids removed from bid response that had a creative size greater than maxWidth/maxHeight",
+		[]string{adapterLabel, successLabel})
+
+	metrics.adapterBidResponseValidationSizeWarn = newCounter(cfg, reg,
+		"adapter_response_validation_size_warn",
+		"Count that tracks number of bids removed from bid response that had a creative size greater than maxWidth/maxHeight (warn)",
+		[]string{adapterLabel, successLabel})
+
+	metrics.adapterBidResponseSecureMarkupError = newCounter(cfg, reg,
+		"adapter_response_validation_secure_err",
+		"Count that tracks number of bids removed from bid response that had a invalid bidAdm",
+		[]string{adapterLabel, successLabel})
+
+	metrics.adapterBidResponseSecureMarkupWarn = newCounter(cfg, reg,
+		"adapter_response_validation_secure_warn",
+		"Count that tracks number of bids removed from bid response that had a invalid bidAdm (warn)",
+		[]string{adapterLabel, successLabel})
+
 	metrics.adapterRequestsTimer = newHistogramVec(cfg, reg,
 		"adapter_request_time_seconds",
 		"Seconds to resolve each successful request labeled by adapter.",
@@ -413,6 +442,26 @@ func NewMetrics(cfg config.PrometheusMetrics, disabledMetrics config.DisabledMet
 		"Count of total requests to Prebid Server that have debug enabled labled by account",
 		[]string{accountLabel})
 
+	metrics.accountBidResponseValidationSizeError = newCounter(cfg, reg,
+		"account_response_validation_size_err",
+		"Count that tracks number of bids removed from bid response that had a creative size greater than maxWidth/maxHeight labeled by account (enforce) ",
+		[]string{accountLabel, successLabel})
+
+	metrics.accountBidResponseValidationSizeWarn = newCounter(cfg, reg,
+		"account_response_validation_size_warn",
+		"Count that tracks number of bids removed from bid response that had a creative size greater than maxWidth/maxHeight labeled by account (warn)",
+		[]string{accountLabel, successLabel})
+
+	metrics.accountBidResponseSecureMarkupError = newCounter(cfg, reg,
+		"account_response_validation_secure_err",
+		"Count that tracks number of bids removed from bid response that had a invalid bidAdm labeled by account (enforce) ",
+		[]string{accountLabel, successLabel})
+
+	metrics.accountBidResponseSecureMarkupWarn = newCounter(cfg, reg,
+		"account_response_validation_secure_warn",
+		"Count that tracks number of bids removed from bid response that had a invalid bidAdm labeled by account (warn)",
+		[]string{accountLabel, successLabel})
+
 	metrics.requestsQueueTimer = newHistogramVec(cfg, reg,
 		"request_queue_time",
 		"Seconds request was waiting in queue",
@@ -897,6 +946,54 @@ func (m *Metrics) RecordAdsCertSignTime(adsCertSignTime time.Duration) {
 	m.adsCertSignTimer.Observe(adsCertSignTime.Seconds())
 }
 
+func (m *Metrics) RecordBidValidationCreativeSizeError(adapter openrtb_ext.BidderName, account string) {
+	m.adapterBidResponseValidationSizeError.With(prometheus.Labels{
+		adapterLabel: string(adapter), successLabel: successLabel,
+	}).Inc()
+
+	if !m.metricsDisabled.AccountAdapterDetails && account != metrics.PublisherUnknown {
+		m.accountBidResponseValidationSizeError.With(prometheus.Labels{
+			accountLabel: account, successLabel: successLabel,
+		}).Inc()
+	}
+}
+
+func (m *Metrics) RecordBidValidationCreativeSizeWarn(adapter openrtb_ext.BidderName, account string) {
+	m.adapterBidResponseValidationSizeWarn.With(prometheus.Labels{
+		adapterLabel: string(adapter), successLabel: successLabel,
+	}).Inc()
+
+	if !m.metricsDisabled.AccountAdapterDetails && account != metrics.PublisherUnknown {
+		m.accountBidResponseValidationSizeWarn.With(prometheus.Labels{
+			accountLabel: account, successLabel: successLabel,
+		}).Inc()
+	}
+}
+
+func (m *Metrics) RecordBidValidationSecureMarkupError(adapter openrtb_ext.BidderName, account string) {
+	m.adapterBidResponseSecureMarkupError.With(prometheus.Labels{
+		adapterLabel: string(adapter), successLabel: successLabel,
+	}).Inc()
+
+	if !m.metricsDisabled.AccountAdapterDetails && account != metrics.PublisherUnknown {
+		m.accountBidResponseSecureMarkupError.With(prometheus.Labels{
+			accountLabel: account, successLabel: successLabel,
+		}).Inc()
+	}
+}
+
+func (m *Metrics) RecordBidValidationSecureMarkupWarn(adapter openrtb_ext.BidderName, account string) {
+	m.adapterBidResponseSecureMarkupWarn.With(prometheus.Labels{
+		adapterLabel: string(adapter), successLabel: successLabel,
+	}).Inc()
+
+	if !m.metricsDisabled.AccountAdapterDetails && account != metrics.PublisherUnknown {
+		m.accountBidResponseSecureMarkupWarn.With(prometheus.Labels{
+			accountLabel: account, successLabel: successLabel,
+		}).Inc()
+	}
+}
+
 func (m *Metrics) RecordModuleCalled(labels metrics.ModuleLabels, duration time.Duration) {
 	m.moduleCalls[labels.Module].With(prometheus.Labels{
 		stageLabel: labels.Stage,
diff --git a/modules/builder.go b/modules/builder.go
index 803ab8bf..ffb814e6 100644
--- a/modules/builder.go
+++ b/modules/builder.go
@@ -1,7 +1,15 @@
 package modules
 
+import (
+	prebidOrtb2blocking "github.com/prebid/prebid-server/modules/prebid/ortb2blocking"
+)
+
 // builders returns mapping between module name and its builder
 // vendor and module names are chosen based on the module directory name
 func builders() ModuleBuilders {
-	return ModuleBuilders{}
+	return ModuleBuilders{
+		"prebid": {
+			"ortb2blocking": prebidOrtb2blocking.Builder,
+		},
+	}
 }
diff --git a/modules/moduledeps/deps.go b/modules/moduledeps/deps.go
new file mode 100644
index 00000000..08b18349
--- /dev/null
+++ b/modules/moduledeps/deps.go
@@ -0,0 +1,9 @@
+package moduledeps
+
+import "net/http"
+
+// ModuleDeps provides dependencies that custom modules may need for hooks execution.
+// Additional dependencies can be added here if modules need something more.
+type ModuleDeps struct {
+	HTTPClient *http.Client
+}
diff --git a/modules/modules.go b/modules/modules.go
index e595e1e1..ac60ec58 100644
--- a/modules/modules.go
+++ b/modules/modules.go
@@ -3,10 +3,11 @@ package modules
 import (
 	"encoding/json"
 	"fmt"
-	"net/http"
 
+	"github.com/golang/glog"
 	"github.com/prebid/prebid-server/config"
 	"github.com/prebid/prebid-server/hooks"
+	"github.com/prebid/prebid-server/modules/moduledeps"
 )
 
 //go:generate go run ./generator/buildergen.go
@@ -23,14 +24,14 @@ type Builder interface {
 	// It returns hook repository created based on the implemented hook interfaces by modules
 	// and a map of modules to a list of stage names for which module provides hooks
 	// or an error encountered during module initialization.
-	Build(cfg config.Modules, client *http.Client) (hooks.HookRepository, map[string][]string, error)
+	Build(cfg config.Modules, client moduledeps.ModuleDeps) (hooks.HookRepository, map[string][]string, error)
 }
 
 type (
 	// ModuleBuilders mapping between module name and its builder: map[vendor]map[module]ModuleBuilderFn
 	ModuleBuilders map[string]map[string]ModuleBuilderFn
-	// ModuleBuilderFn returns an interface{} type that implements certain hook interfaces
-	ModuleBuilderFn func(cfg json.RawMessage, client *http.Client) (interface{}, error)
+	// ModuleBuilderFn returns an interface{} type that implements certain hook interfaces.
+	ModuleBuilderFn func(cfg json.RawMessage, deps moduledeps.ModuleDeps) (interface{}, error)
 )
 
 type builder struct {
@@ -44,21 +45,36 @@ type builder struct {
 //
 // Method returns a hooks.HookRepository and a map of modules to a list of stage names
 // for which module provides hooks or an error occurred during modules initialization.
-func (m *builder) Build(cfg config.Modules, client *http.Client) (hooks.HookRepository, map[string][]string, error) {
+func (m *builder) Build(
+	cfg config.Modules,
+	deps moduledeps.ModuleDeps,
+) (hooks.HookRepository, map[string][]string, error) {
 	modules := make(map[string]interface{})
 	for vendor, moduleBuilders := range m.builders {
 		for moduleName, builder := range moduleBuilders {
 			var err error
 			var conf json.RawMessage
+			var isEnabled bool
 
 			id := fmt.Sprintf("%s.%s", vendor, moduleName)
 			if data, ok := cfg[vendor][moduleName]; ok {
 				if conf, err = json.Marshal(data); err != nil {
 					return nil, nil, fmt.Errorf(`failed to marshal "%s" module config: %s`, id, err)
 				}
+
+				if values, ok := data.(map[string]interface{}); ok {
+					if value, ok := values["enabled"].(bool); ok {
+						isEnabled = value
+					}
+				}
+			}
+
+			if !isEnabled {
+				glog.Infof("Skip %s module, disabled.", id)
+				continue
 			}
 
-			module, err := builder(conf, client)
+			module, err := builder(conf, deps)
 			if err != nil {
 				return nil, nil, fmt.Errorf(`failed to init "%s" module: %s`, id, err)
 			}
diff --git a/modules/prebid/ortb2blocking/README.md b/modules/prebid/ortb2blocking/README.md
new file mode 100644
index 00000000..a2176ca5
--- /dev/null
+++ b/modules/prebid/ortb2blocking/README.md
@@ -0,0 +1,18 @@
+# Overview
+
+Prebid Server doesn't provide all services an SSP normally provides, including account configuration of blocked:
+
+- advertiser domains
+- advertiser categories
+- creative types
+- creative attributes
+- apps
+
+This module allows Prebid Server host companies to better support adapters that require blocking config.
+
+# Maintainer contacts
+
+Any suggestions or questions can be directed to [example@site.com]() e-mail.
+
+Or just open new [issue](https://github.com/prebid/prebid-server/issues/new)
+or [pull request](https://github.com/prebid/prebid-server/pulls) in this repository.
diff --git a/modules/prebid/ortb2blocking/config.go b/modules/prebid/ortb2blocking/config.go
new file mode 100644
index 00000000..a7c6caff
--- /dev/null
+++ b/modules/prebid/ortb2blocking/config.go
@@ -0,0 +1,134 @@
+package ortb2blocking
+
+import (
+	"encoding/json"
+	"fmt"
+
+	"github.com/prebid/openrtb/v17/adcom1"
+)
+
+func newConfig(data json.RawMessage) (config, error) {
+	var cfg config
+	if err := json.Unmarshal(data, &cfg); err != nil {
+		return cfg, fmt.Errorf("failed to parse config: %s", err)
+	}
+	return cfg, nil
+}
+
+type config struct {
+	Attributes Attributes `json:"attributes"`
+}
+
+type Attributes struct {
+	Badv  Badv  `json:"badv"`
+	Bcat  Bcat  `json:"bcat"`
+	Bapp  Bapp  `json:"bapp"`
+	Btype Btype `json:"btype"`
+	Battr Battr `json:"battr"`
+}
+
+type Badv struct {
+	ActionOverrides        BadvActionOverride `json:"action_overrides"`
+	AllowedAdomainForDeals []string           `json:"allowed_adomain_for_deals"`
+	BlockedAdomain         []string           `json:"blocked_adomain"`
+	BlockUnknownAdomain    bool               `json:"block_unknown_adomain"`
+	EnforceBlocks          bool               `json:"enforce_blocks"`
+}
+
+type BadvActionOverride struct {
+	AllowedAdomainForDeals []ActionOverride `json:"allowed_adomain_for_deals"`
+	BlockedAdomain         []ActionOverride `json:"blocked_adomain"`
+	BlockUnknownAdomain    []ActionOverride `json:"block_unknown_adomain"`
+	EnforceBlocks          []ActionOverride `json:"enforce_blocks"`
+}
+
+type Bcat struct {
+	ActionOverrides       BcatActionOverride      `json:"action_overrides"`
+	AllowedAdvCatForDeals []string                `json:"allowed_adv_cat_for_deals"`
+	BlockedAdvCat         []string                `json:"blocked_adv_cat"`
+	BlockUnknownAdvCat    bool                    `json:"block_unknown_adv_cat"`
+	CategoryTaxonomy      adcom1.CategoryTaxonomy `json:"category_taxonomy"`
+	EnforceBlocks         bool                    `json:"enforce_blocks"`
+}
+
+type BcatActionOverride struct {
+	AllowedAdvCatForDeals []ActionOverride `json:"allowed_adv_cat_for_deals"`
+	BlockedAdvCat         []ActionOverride `json:"blocked_adv_cat"`
+	BlockUnknownAdvCat    []ActionOverride `json:"block_unknown_adv_cat"`
+	EnforceBlocks         []ActionOverride `json:"enforce_blocks"`
+}
+
+type Bapp struct {
+	ActionOverrides    BappActionOverride `json:"action_overrides"`
+	AllowedAppForDeals []string           `json:"allowed_app_for_deals"`
+	BlockedApp         []string           `json:"blocked_app"`
+	EnforceBlocks      bool               `json:"enforce_blocks"`
+}
+
+type BappActionOverride struct {
+	AllowedAppForDeals []ActionOverride `json:"allowed_app_for_deals"`
+	BlockedApp         []ActionOverride `json:"blocked_app"`
+	EnforceBlocks      []ActionOverride `json:"enforce_blocks"`
+}
+
+type Btype struct {
+	ActionOverrides   BtypeActionOverride `json:"action_overrides"`
+	BlockedBannerType []int               `json:"blocked_banner_type"`
+}
+
+type BtypeActionOverride struct {
+	BlockedBannerType []ActionOverride `json:"blocked_banner_type"`
+}
+
+type Battr struct {
+	ActionOverrides           BattrActionOverride `json:"action_overrides"`
+	AllowedBannerAttrForDeals []int               `json:"allowed_banner_attr_for_deals"`
+	BlockedBannerAttr         []int               `json:"blocked_banner_attr"`
+	EnforceBlocks             bool                `json:"enforce_blocks"`
+}
+
+type BattrActionOverride struct {
+	AllowedBannerAttrForDeals []ActionOverride `json:"allowed_banner_attr_for_deals"`
+	BlockedBannerAttr         []ActionOverride `json:"blocked_banner_attr"`
+	EnforceBlocks             []ActionOverride `json:"enforce_blocks"`
+}
+
+type ActionOverride struct {
+	Conditions Conditions `json:"conditions"`
+	Override   Override   `json:"override"`
+}
+
+type Conditions struct {
+	Bidders    []string `json:"bidders"`
+	MediaTypes []string `json:"media_types"`
+	DealIds    []string `json:"deal_ids"`
+}
+
+type Override struct {
+	IsActive bool
+	Ids      []int
+	Names    []string
+}
+
+func (o *Override) UnmarshalJSON(bytes []byte) error {
+	var overrideData interface{}
+	if err := json.Unmarshal(bytes, &overrideData); err != nil {
+		return err
+	}
+
+	switch overrideValue := overrideData.(type) {
+	case bool:
+		o.IsActive = overrideValue
+	case []interface{}:
+		for _, value := range overrideValue {
+			switch override := value.(type) {
+			case string:
+				o.Names = append(o.Names, override)
+			case float64:
+				o.Ids = append(o.Ids, int(override))
+			}
+		}
+	}
+
+	return nil
+}
diff --git a/modules/prebid/ortb2blocking/module.go b/modules/prebid/ortb2blocking/module.go
new file mode 100644
index 00000000..f0ef80cc
--- /dev/null
+++ b/modules/prebid/ortb2blocking/module.go
@@ -0,0 +1,13 @@
+package ortb2blocking
+
+import (
+	"encoding/json"
+
+	"github.com/prebid/prebid-server/modules/moduledeps"
+)
+
+func Builder(_ json.RawMessage, _ moduledeps.ModuleDeps) (interface{}, error) {
+	return Module{}, nil
+}
+
+type Module struct{}
diff --git a/openrtb_ext/imp_bliink.go b/openrtb_ext/imp_bliink.go
new file mode 100644
index 00000000..71745dee
--- /dev/null
+++ b/openrtb_ext/imp_bliink.go
@@ -0,0 +1,6 @@
+package openrtb_ext
+
+type ImpExtBliink struct {
+	TagID    string `json:"tagId"`
+	ImageUrl string `json:"imageUrl"`
+}
diff --git a/openrtb_ext/request_wrapper.go b/openrtb_ext/request_wrapper.go
index 63d16185..526d86f0 100644
--- a/openrtb_ext/request_wrapper.go
+++ b/openrtb_ext/request_wrapper.go
@@ -1273,6 +1273,8 @@ type ImpExt struct {
 	extDirty    bool
 	prebid      *ExtImpPrebid
 	prebidDirty bool
+	tid         string
+	tidDirty    bool
 }
 
 func (e *ImpExt) unmarshal(extJson json.RawMessage) error {
@@ -1298,6 +1300,13 @@ func (e *ImpExt) unmarshal(extJson json.RawMessage) error {
 		}
 	}
 
+	tidJson, hasTid := e.ext["tid"]
+	if hasTid {
+		if err := json.Unmarshal(tidJson, &e.tid); err != nil {
+			return err
+		}
+	}
+
 	return nil
 }
 
@@ -1319,6 +1328,19 @@ func (e *ImpExt) marshal() (json.RawMessage, error) {
 		e.prebidDirty = false
 	}
 
+	if e.tidDirty {
+		if len(e.tid) > 0 {
+			tidJson, err := json.Marshal(e.tid)
+			if err != nil {
+				return nil, err
+			}
+			e.ext["tid"] = tidJson
+		} else {
+			delete(e.ext, "tid")
+		}
+		e.tidDirty = false
+	}
+
 	e.extDirty = false
 	if len(e.ext) == 0 {
 		return nil, nil
@@ -1327,7 +1349,7 @@ func (e *ImpExt) marshal() (json.RawMessage, error) {
 }
 
 func (e *ImpExt) Dirty() bool {
-	return e.extDirty || e.prebidDirty
+	return e.extDirty || e.prebidDirty || e.tidDirty
 }
 
 func (e *ImpExt) GetExt() map[string]json.RawMessage {
@@ -1363,6 +1385,16 @@ func (e *ImpExt) SetPrebid(prebid *ExtImpPrebid) {
 	e.prebidDirty = true
 }
 
+func (e *ImpExt) GetTid() string {
+	tid := e.tid
+	return tid
+}
+
+func (e *ImpExt) SetTid(tid string) {
+	e.tid = tid
+	e.tidDirty = true
+}
+
 func CreateImpExtForTesting(ext map[string]json.RawMessage, prebid *ExtImpPrebid) ImpExt {
 	return ImpExt{ext: ext, prebid: prebid}
 }
diff --git a/router/router.go b/router/router.go
index 81658728..b93473b4 100644
--- a/router/router.go
+++ b/router/router.go
@@ -25,6 +25,7 @@ import (
 	"github.com/prebid/prebid-server/metrics"
 	metricsConf "github.com/prebid/prebid-server/metrics/config"
 	"github.com/prebid/prebid-server/modules"
+	"github.com/prebid/prebid-server/modules/moduledeps"
 	"github.com/prebid/prebid-server/openrtb_ext"
 	"github.com/prebid/prebid-server/pbs"
 	pbc "github.com/prebid/prebid-server/prebid_cache_client"
@@ -168,7 +169,8 @@ func New(cfg *config.Configuration, rateConvertor *currency.RateConverter) (r *R
 		syncerKeys = append(syncerKeys, k)
 	}
 
-	repo, moduleStageNames, err := modules.NewBuilder().Build(cfg.Hooks.Modules, generalHttpClient)
+	moduleDeps := moduledeps.ModuleDeps{HTTPClient: generalHttpClient}
+	repo, moduleStageNames, err := modules.NewBuilder().Build(cfg.Hooks.Modules, moduleDeps)
 	if err != nil {
 		glog.Fatalf("Failed to init hook modules: %v", err)
 	}
diff --git a/static/bidder-info/improvedigital.yaml b/static/bidder-info/improvedigital.yaml
index 4a990e24..d14356cd 100644
--- a/static/bidder-info/improvedigital.yaml
+++ b/static/bidder-info/improvedigital.yaml
@@ -1,4 +1,4 @@
-endpoint: "http://ad.360yield.com/pbs"
+endpoint: "http://ad.360yield.com/{PublisherId}pbs"
 maintainer:
   email: "hb@azerion.com"
 gvlVendorID: 253
diff --git a/static/bidder-params/bliink.json b/static/bidder-params/bliink.json
index 00621dee..831f71d0 100644
--- a/static/bidder-params/bliink.json
+++ b/static/bidder-params/bliink.json
@@ -4,5 +4,15 @@
   "description": "A schema which validates params accepted by the Bliink adapter",
   "type": "object",
 
-  "properties": {}
+  "properties": {
+    "tagId": {
+      "type": "string",
+      "description": "The TagID from BLIINK"
+    },
+    "imageUrl": {
+      "type": "string",
+      "description": "The image url on which the ad is displayed in case of in-image ad"
+    }
+  },
+  "required": ["tagId"]
 }
\ No newline at end of file
diff --git a/static/bidder-params/visx.json b/static/bidder-params/visx.json
index 7ec6a5ee..fe606f93 100644
--- a/static/bidder-params/visx.json
+++ b/static/bidder-params/visx.json
@@ -5,7 +5,8 @@
   "type": "object",
   "properties": {
     "uid": {
-      "type": "integer",
+      "type": ["integer", "string"],
+      "pattern": "^\\d+$",
       "description": "An ID which identifies this placement of the impression"
     },
     "size": {


Test patch:
diff --git a/adapters/bliink/params_test.go b/adapters/bliink/params_test.go
new file mode 100644
index 00000000..dc45a324
--- /dev/null
+++ b/adapters/bliink/params_test.go
@@ -0,0 +1,44 @@
+package bliink
+
+import (
+	"encoding/json"
+	"github.com/prebid/prebid-server/openrtb_ext"
+	"testing"
+)
+
+var validParams = []string{
+	`{"tagId": "hash", "imageUrl": "https://www.test.com"}`,
+}
+
+func TestValidParams(t *testing.T) {
+	validator, err := openrtb_ext.NewBidderParamsValidator("../../static/bidder-params")
+	if err != nil {
+		t.Fatalf("Failed to fetch the json schema. %v", err)
+	}
+
+	for _, validParam := range validParams {
+		if err := validator.Validate(openrtb_ext.BidderBliink, json.RawMessage(validParam)); err != nil {
+			t.Errorf("Schema rejected valid params: %s", validParam)
+		}
+	}
+}
+
+var invalidParams = []string{
+	``,
+	`null`,
+	`{"tagId": 32}`,
+	`{"imageUrl": 32}`,
+}
+
+func TestInvalidParams(t *testing.T) {
+	validator, err := openrtb_ext.NewBidderParamsValidator("../../static/bidder-params")
+	if err != nil {
+		t.Fatalf("Failed to fetch the json schema. %v", err)
+	}
+
+	for _, invalidParam := range invalidParams {
+		if err := validator.Validate(openrtb_ext.BidderBliink, json.RawMessage(invalidParam)); err == nil {
+			t.Errorf("Schema allowed invalid params: %s", invalidParam)
+		}
+	}
+}
diff --git a/adapters/improvedigital/improvedigital_test.go b/adapters/improvedigital/improvedigital_test.go
index f648319c..15ca5d5f 100644
--- a/adapters/improvedigital/improvedigital_test.go
+++ b/adapters/improvedigital/improvedigital_test.go
@@ -10,7 +10,7 @@ import (
 
 func TestJsonSamples(t *testing.T) {
 	bidder, buildErr := Builder(openrtb_ext.BidderImprovedigital, config.Adapter{
-		Endpoint: "http://localhost/pbs"}, config.Server{ExternalUrl: "http://hosturl.com", GvlID: 1, DataCenter: "2"})
+		Endpoint: "http://localhost/{PublisherId}pbs"}, 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/improvedigital/improvedigitaltest/supplemental/ad-server-url.json b/adapters/improvedigital/improvedigitaltest/supplemental/ad-server-url.json
new file mode 100644
index 00000000..4df449cf
--- /dev/null
+++ b/adapters/improvedigital/improvedigitaltest/supplemental/ad-server-url.json
@@ -0,0 +1,140 @@
+{
+  "mockBidRequest": {
+    "id": "test-request-id",
+    "site": {
+      "page": "https://good.site/url",
+      "domain": "good.site",
+      "publisher": {
+        "id": "uniq_pub_id"
+      },
+      "keywords": "omgword",
+      "ext": {
+        "amp": 0
+      }
+    },
+    "imp": [
+      {
+        "id": "test-imp-id-banner",
+        "banner": {
+          "format": [
+            {
+              "w": 300,
+              "h": 250
+            }
+          ]
+        },
+        "ext": {
+          "bidder": {
+            "placementId": 13245,
+            "publisherId": 1234
+          }
+        }
+      }
+    ]
+  },
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "http://localhost/1234/pbs",
+        "body": {
+          "id": "test-request-id",
+          "site": {
+            "page": "https://good.site/url",
+            "domain": "good.site",
+            "publisher": {
+              "id": "uniq_pub_id"
+            },
+            "keywords": "omgword",
+            "ext": {
+              "amp": 0
+            }
+          },
+          "imp": [
+            {
+              "id": "test-imp-id-banner",
+              "banner": {
+                "format": [
+                  {
+                    "w": 300,
+                    "h": 250
+                  }
+                ]
+              },
+              "ext": {
+                "bidder": {
+                  "placementId": 13245,
+                  "publisherId": 1234
+                }
+              }
+            }
+          ]
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "id": "test-request-id",
+          "seatbid": [
+            {
+              "seat": "improvedigital",
+              "bid": [
+                {
+                  "id": "randomid1",
+                  "impid": "test-imp-id-banner",
+                  "price": 0.5,
+                  "adid": "12345678",
+                  "adm": "some-test-ad-html",
+                  "cid": "987",
+                  "crid": "12345678",
+                  "h": 250,
+                  "w": 300,
+                  "ext": {
+                    "improvedigital": {
+                      "line_item_id": 279820,
+                      "bidder_id": 0,
+                      "brand_name": "",
+                      "buying_type": "classic",
+                      "agency_id": "0"
+                    }
+                  }
+                }
+              ]
+            }
+          ],
+          "cur": "USD"
+        }
+      }
+    }
+  ],
+  "expectedBidResponses": [
+    {
+      "currency": "USD",
+      "bids": [
+        {
+          "bid": {
+            "id": "randomid1",
+            "impid": "test-imp-id-banner",
+            "price": 0.5,
+            "adm": "some-test-ad-html",
+            "adid": "12345678",
+            "cid": "987",
+            "crid": "12345678",
+            "w": 300,
+            "h": 250,
+            "dealid": "279820",
+            "ext": {
+              "improvedigital": {
+                "line_item_id": 279820,
+                "bidder_id": 0,
+                "brand_name": "",
+                "buying_type": "classic",
+                "agency_id": "0"
+              }
+            }
+          },
+          "type": "banner"
+        }
+      ]
+    }
+  ]
+}
diff --git a/adapters/visx/params_test.go b/adapters/visx/params_test.go
index e857e8d2..e53d2cda 100644
--- a/adapters/visx/params_test.go
+++ b/adapters/visx/params_test.go
@@ -2,8 +2,9 @@ package visx
 
 import (
 	"encoding/json"
-	"github.com/prebid/prebid-server/openrtb_ext"
 	"testing"
+
+	"github.com/prebid/prebid-server/openrtb_ext"
 )
 
 func TestValidParams(t *testing.T) {
@@ -34,6 +35,7 @@ func TestInvalidParams(t *testing.T) {
 
 var validParams = []string{
 	`{"uid":13245}`,
+	`{"uid":"13245"}`,
 	`{"uid":13245, "size": [10,5]}`,
 	`{"uid":13245, "other_optional": true}`,
 }
@@ -47,7 +49,10 @@ var invalidParams = []string{
 	`2`,
 	`{"size":12345678}`,
 	`{"size":""}`,
-	`{"uid": "1"}`,
+	`{"uid": "-1"}`,
+	`{"uid": "232af"}`,
+	`{"uid": "af213"}`,
+	`{"uid": "af"}`,
 	`{"size": true}`,
 	`{"uid": true, "size":"1234567"}`,
 }
diff --git a/adapters/yahoossp/yahoossptest/supplemental/simple-banner-gpp-overwrite.json b/adapters/yahoossp/yahoossptest/supplemental/simple-banner-gpp-overwrite.json
new file mode 100644
index 00000000..ffc7ae65
--- /dev/null
+++ b/adapters/yahoossp/yahoossptest/supplemental/simple-banner-gpp-overwrite.json
@@ -0,0 +1,132 @@
+{
+  "mockBidRequest": {
+    "id": "test-request-id",
+    "imp": [
+      {
+        "id": "test-imp-id",
+        "banner": {
+          "format": [{"w": 728, "h": 90}]
+        },
+        "tagid": "123",
+        "ext": {
+          "bidder": {
+            "dcn": "dcn1",
+            "pos": "pos2"
+          }
+        }
+      }
+    ],
+    "device": {
+      "ua": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36",
+      "ip": "127.0.0.1"
+    },
+    "site": {
+      "id": "test-site-id"
+    },
+    "regs": {
+      "gpp": "header~1YNN",
+      "gpp_sid": [6],
+      "ext": {
+        "param1": "val1",
+        "gpp": "overwritten"
+      }
+    }
+  },
+
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "headers": {
+          "Accept": [
+            "application/json"
+          ],
+          "Content-Type": [
+            "application/json;charset=utf-8"
+          ],
+          "User-Agent": [
+            "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36"
+          ],
+          "X-Openrtb-Version": [
+            "2.5"
+          ]
+        },
+        "uri": "",
+        "body": {
+          "device": {
+            "ua": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36",
+            "ip": "127.0.0.1"
+          },
+          "id": "test-request-id",
+          "imp": [
+            {
+              "id": "test-imp-id",
+              "tagid": "pos2",
+              "banner": {
+                "format": [{"w": 728, "h": 90}],
+                "w": 728,
+                "h": 90
+              },
+              "ext": {
+                "bidder": {
+                  "dcn": "dcn1",
+                  "pos": "pos2"
+                }
+              }
+            }
+          ],
+          "site": {
+            "id": "dcn1"
+          },
+          "regs": {
+            "ext": {
+              "gpp": "header~1YNN",
+              "gpp_sid": [6],
+              "param1": "val1"
+            }
+          }
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "id": "test-request-id",
+          "seatbid": [
+            {
+              "seat": "yahoossp",
+              "bid": [{
+                "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800",
+                "impid": "test-imp-id",
+                "price": 0.500000,
+                "adm": "some-test-ad",
+                "crid": "crid_10",
+                "h": 90,
+                "w": 728
+              }]
+            }
+          ],
+          "cur": "USD"
+        }
+      }
+    }
+  ],
+
+  "expectedBidResponses": [
+    {
+      "currency": "USD",
+      "bids": [
+        {
+          "bid": {
+            "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800",
+            "impid": "test-imp-id",
+            "price": 0.5,
+            "adm": "some-test-ad",
+            "crid": "crid_10",
+            "w": 728,
+            "h": 90
+          },
+          "type": "banner"
+        }
+      ]
+    }
+  ]
+}
diff --git a/adapters/yahoossp/yahoossptest/supplemental/simple-banner-gpp.json b/adapters/yahoossp/yahoossptest/supplemental/simple-banner-gpp.json
new file mode 100644
index 00000000..88f97d11
--- /dev/null
+++ b/adapters/yahoossp/yahoossptest/supplemental/simple-banner-gpp.json
@@ -0,0 +1,127 @@
+{
+  "mockBidRequest": {
+    "id": "test-request-id",
+    "imp": [
+      {
+        "id": "test-imp-id",
+        "banner": {
+          "format": [{"w": 728, "h": 90}]
+        },
+        "tagid": "123",
+        "ext": {
+          "bidder": {
+            "dcn": "dcn1",
+            "pos": "pos2"
+          }
+        }
+      }
+    ],
+    "device": {
+      "ua": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36",
+      "ip": "127.0.0.1"
+    },
+    "site": {
+      "id": "test-site-id"
+    },
+    "regs": {
+      "gpp": "header~1YNN",
+      "gpp_sid": [6]
+    }
+  },
+
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "headers": {
+          "Accept": [
+            "application/json"
+          ],
+          "Content-Type": [
+            "application/json;charset=utf-8"
+          ],
+          "User-Agent": [
+            "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36"
+          ],
+          "X-Openrtb-Version": [
+            "2.5"
+          ]
+        },
+        "uri": "",
+        "body": {
+          "device": {
+            "ua": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36",
+            "ip": "127.0.0.1"
+          },
+          "id": "test-request-id",
+          "imp": [
+            {
+              "id": "test-imp-id",
+              "tagid": "pos2",
+              "banner": {
+                "format": [{"w": 728, "h": 90}],
+                "w": 728,
+                "h": 90
+              },
+              "ext": {
+                "bidder": {
+                  "dcn": "dcn1",
+                  "pos": "pos2"
+                }
+              }
+            }
+          ],
+          "site": {
+            "id": "dcn1"
+          },
+          "regs": {
+            "ext": {
+              "gpp": "header~1YNN",
+              "gpp_sid": [6]
+            }
+          }
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "id": "test-request-id",
+          "seatbid": [
+            {
+              "seat": "yahoossp",
+              "bid": [{
+                "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800",
+                "impid": "test-imp-id",
+                "price": 0.500000,
+                "adm": "some-test-ad",
+                "crid": "crid_10",
+                "h": 90,
+                "w": 728
+              }]
+            }
+          ],
+          "cur": "USD"
+        }
+      }
+    }
+  ],
+
+  "expectedBidResponses": [
+    {
+      "currency": "USD",
+      "bids": [
+        {
+          "bid": {
+            "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800",
+            "impid": "test-imp-id",
+            "price": 0.5,
+            "adm": "some-test-ad",
+            "crid": "crid_10",
+            "w": 728,
+            "h": 90
+          },
+          "type": "banner"
+        }
+      ]
+    }
+  ]
+}
diff --git a/config/config_test.go b/config/config_test.go
index 3298bec9..43ab9273 100644
--- a/config/config_test.go
+++ b/config/config_test.go
@@ -175,6 +175,10 @@ func TestDefaults(t *testing.T) {
 	cmpNils(t, "host_schain_node", cfg.HostSChainNode)
 	cmpStrings(t, "datacenter", cfg.DataCenter, "")
 	cmpBools(t, "hooks.enabled", cfg.Hooks.Enabled, false)
+	cmpStrings(t, "validations.banner_creative_max_size", cfg.Validations.BannerCreativeMaxSize, "skip")
+	cmpStrings(t, "validations.secure_markup", cfg.Validations.SecureMarkup, "skip")
+	cmpInts(t, "validations.max_creative_width", int(cfg.Validations.MaxCreativeWidth), 0)
+	cmpInts(t, "validations.max_creative_height", int(cfg.Validations.MaxCreativeHeight), 0)
 	cmpBools(t, "account_modules_metrics", cfg.Metrics.Disabled.AccountModulesMetrics, false)
 
 	//Assert purpose VendorExceptionMap hash tables were built correctly
@@ -409,6 +413,11 @@ host_schain_node:
     sid: "00001"
     rid: "BidRequest"
     hp: 1
+validations:
+    banner_creative_max_size: "skip"
+    secure_markup: "skip"
+    max_creative_width: 0
+    max_creative_height: 0
 experiment:
     adscert:
         mode: inprocess
@@ -497,6 +506,10 @@ func TestFullConfig(t *testing.T) {
 	cmpStrings(t, "host_schain_node.rid", cfg.HostSChainNode.RID, "BidRequest")
 	cmpInt8s(t, "host_schain_node.hp", cfg.HostSChainNode.HP, &int8One)
 	cmpStrings(t, "datacenter", cfg.DataCenter, "1")
+	cmpStrings(t, "validations.banner_creative_max_size", cfg.Validations.BannerCreativeMaxSize, "skip")
+	cmpStrings(t, "validations.secure_markup", cfg.Validations.SecureMarkup, "skip")
+	cmpInts(t, "validations.max_creative_width", int(cfg.Validations.MaxCreativeWidth), 0)
+	cmpInts(t, "validations.max_creative_height", int(cfg.Validations.MaxCreativeHeight), 0)
 
 	//Assert the NonStandardPublishers was correctly unmarshalled
 	assert.Equal(t, []string{"pub1", "pub2"}, cfg.GDPR.NonStandardPublishers, "gdpr.non_standard_publishers")
diff --git a/endpoints/openrtb2/auction_test.go b/endpoints/openrtb2/auction_test.go
index b32f1349..16ae06bb 100644
--- a/endpoints/openrtb2/auction_test.go
+++ b/endpoints/openrtb2/auction_test.go
@@ -1113,7 +1113,6 @@ func TestStoredRequests(t *testing.T) {
 		}
 		expectJson := json.RawMessage(testFinalRequests[i])
 		assert.JSONEq(t, string(expectJson), string(newRequest), "Incorrect result request %d", i)
-
 		expectedImp := testStoredImpIds[i]
 		expectedStoredImp := json.RawMessage(testStoredImps[i])
 		if len(impExtInfoMap[expectedImp].StoredImp) > 0 {
@@ -1632,7 +1631,7 @@ func TestValidateRequest(t *testing.T) {
 	}
 
 	for _, test := range testCases {
-		errorList := deps.validateRequest(test.givenRequestWrapper, test.givenIsAmp, false, nil)
+		errorList := deps.validateRequest(test.givenRequestWrapper, test.givenIsAmp, false, nil, false)
 		assert.Equalf(t, test.expectedErrorList, errorList, "Error doesn't match: %s\n", test.description)
 
 		if len(errorList) == 0 {
@@ -2454,7 +2453,7 @@ func TestCurrencyTrunc(t *testing.T) {
 		Cur: []string{"USD", "EUR"},
 	}
 
-	errL := deps.validateRequest(&openrtb_ext.RequestWrapper{BidRequest: &req}, false, false, nil)
+	errL := deps.validateRequest(&openrtb_ext.RequestWrapper{BidRequest: &req}, false, false, nil, false)
 
 	expectedError := errortypes.Warning{Message: "A prebid request can only process one currency. Taking the first currency in the list, USD, as the active currency"}
 	assert.ElementsMatch(t, errL, []error{&expectedError})
@@ -2503,7 +2502,7 @@ func TestCCPAInvalid(t *testing.T) {
 		},
 	}
 
-	errL := deps.validateRequest(&openrtb_ext.RequestWrapper{BidRequest: &req}, false, false, nil)
+	errL := deps.validateRequest(&openrtb_ext.RequestWrapper{BidRequest: &req}, false, false, nil, false)
 
 	expectedWarning := errortypes.Warning{
 		Message:     "CCPA consent is invalid and will be ignored. (request.regs.ext.us_privacy must contain 4 characters)",
@@ -2555,7 +2554,7 @@ func TestNoSaleInvalid(t *testing.T) {
 		Ext: json.RawMessage(`{"prebid": {"nosale": ["*", "appnexus"]} }`),
 	}
 
-	errL := deps.validateRequest(&openrtb_ext.RequestWrapper{BidRequest: &req}, false, false, nil)
+	errL := deps.validateRequest(&openrtb_ext.RequestWrapper{BidRequest: &req}, false, false, nil, false)
 
 	expectedError := errors.New("request.ext.prebid.nosale is invalid: can only specify all bidders if no other bidders are provided")
 	assert.ElementsMatch(t, errL, []error{expectedError})
@@ -2605,7 +2604,7 @@ func TestValidateSourceTID(t *testing.T) {
 		},
 	}
 
-	deps.validateRequest(&openrtb_ext.RequestWrapper{BidRequest: &req}, false, false, nil)
+	deps.validateRequest(&openrtb_ext.RequestWrapper{BidRequest: &req}, false, false, nil, false)
 	assert.NotEmpty(t, req.Source.TID, "Expected req.Source.TID to be filled with a randomly generated UID")
 }
 
@@ -2650,7 +2649,7 @@ func TestSChainInvalid(t *testing.T) {
 		Ext: json.RawMessage(`{"prebid":{"schains":[{"bidders":["appnexus"],"schain":{"complete":1,"nodes":[{"asi":"directseller1.com","sid":"00001","rid":"BidRequest1","hp":1}],"ver":"1.0"}}, {"bidders":["appnexus"],"schain":{"complete":1,"nodes":[{"asi":"directseller2.com","sid":"00002","rid":"BidRequest2","hp":1}],"ver":"1.0"}}]}}`),
 	}
 
-	errL := deps.validateRequest(&openrtb_ext.RequestWrapper{BidRequest: &req}, false, false, nil)
+	errL := deps.validateRequest(&openrtb_ext.RequestWrapper{BidRequest: &req}, false, false, nil, false)
 
 	expectedError := errors.New("request.ext.prebid.schains contains multiple schains for bidder appnexus; it must contain no more than one per bidder.")
 	assert.ElementsMatch(t, errL, []error{expectedError})
@@ -2921,42 +2920,185 @@ func TestSanitizeRequest(t *testing.T) {
 func TestValidateAndFillSourceTID(t *testing.T) {
 	testTID := "some-tid"
 	testCases := []struct {
-		description   string
-		req           *openrtb2.BidRequest
-		expectRandTID bool
-		expectedTID   string
+		description         string
+		req                 *openrtb_ext.RequestWrapper
+		generateRequestID   bool
+		hasStoredBidRequest bool
+		isAmp               bool
+		expectRandImpTID    bool
+		expectRandSourceTID bool
+		expectSourceTid     *string
+		expectImpTid        *string
 	}{
 		{
-			description:   "req.Source not present. Expecting a randomly generated TID value",
-			req:           &openrtb2.BidRequest{},
-			expectRandTID: true,
+			description: "req source.tid not set, expect random value",
+			req: &openrtb_ext.RequestWrapper{
+				BidRequest: &openrtb2.BidRequest{
+					ID:     "1",
+					Imp:    []openrtb2.Imp{{ID: "1"}},
+					Source: &openrtb2.Source{},
+				},
+			},
+			generateRequestID:   false,
+			hasStoredBidRequest: false,
+			isAmp:               false,
+			expectRandSourceTID: true,
+			expectRandImpTID:    false,
 		},
 		{
-			description: "req.Source.TID not present. Expecting a randomly generated TID value",
-			req: &openrtb2.BidRequest{
-				Source: &openrtb2.Source{},
+			description: "req source.tid set to {{UUID}}, expect to be replaced by random value",
+			req: &openrtb_ext.RequestWrapper{
+				BidRequest: &openrtb2.BidRequest{
+					ID:     "1",
+					Imp:    []openrtb2.Imp{{ID: "1"}},
+					Source: &openrtb2.Source{TID: "{{UUID}}"},
+				},
 			},
-			expectRandTID: true,
+			generateRequestID:   false,
+			hasStoredBidRequest: false,
+			isAmp:               false,
+			expectRandSourceTID: true,
+			expectRandImpTID:    false,
 		},
 		{
-			description: "req.Source.TID present. Expecting no change",
-			req: &openrtb2.BidRequest{
-				Source: &openrtb2.Source{
-					TID: testTID,
+			description: "req source.tid is set, isAmp = true, generateRequestID = true, expect to be replaced by random value",
+			req: &openrtb_ext.RequestWrapper{
+				BidRequest: &openrtb2.BidRequest{
+					ID:     "1",
+					Imp:    []openrtb2.Imp{{ID: "1"}},
+					Source: &openrtb2.Source{TID: "test-tid"},
+				},
+			},
+			generateRequestID:   true,
+			hasStoredBidRequest: false,
+			isAmp:               true,
+			expectRandSourceTID: true,
+			expectRandImpTID:    false,
+		},
+		{
+			description: "req source.tid is set,  hasStoredBidRequest = true, generateRequestID = true, expect to be replaced by random value",
+			req: &openrtb_ext.RequestWrapper{
+				BidRequest: &openrtb2.BidRequest{
+					ID:     "1",
+					Imp:    []openrtb2.Imp{{ID: "1"}},
+					Source: &openrtb2.Source{TID: "test-tid"},
+				},
+			},
+			generateRequestID:   true,
+			hasStoredBidRequest: true,
+			isAmp:               false,
+			expectRandSourceTID: true,
+			expectRandImpTID:    false,
+		},
+		{
+			description: "req source.tid is set,  hasStoredBidRequest = true, generateRequestID = false, expect NOT to be replaced by random value",
+			req: &openrtb_ext.RequestWrapper{
+				BidRequest: &openrtb2.BidRequest{
+					ID:     "1",
+					Imp:    []openrtb2.Imp{{ID: "1"}},
+					Source: &openrtb2.Source{TID: testTID},
+				},
+			},
+			generateRequestID:   false,
+			hasStoredBidRequest: true,
+			isAmp:               false,
+			expectRandSourceTID: false,
+			expectRandImpTID:    false,
+			expectSourceTid:     &testTID,
+		},
+		{
+			description: "req imp.ext.tid not set, expect random value",
+			req: &openrtb_ext.RequestWrapper{
+				BidRequest: &openrtb2.BidRequest{
+					ID:     "1",
+					Imp:    []openrtb2.Imp{{ID: "1"}},
+					Source: &openrtb2.Source{},
+				},
+			},
+			generateRequestID:   false,
+			hasStoredBidRequest: false,
+			isAmp:               false,
+			expectRandSourceTID: false,
+			expectRandImpTID:    true,
+		},
+		{
+			description: "req imp.ext.tid set to {{UUID}}, expect random value",
+			req: &openrtb_ext.RequestWrapper{
+				BidRequest: &openrtb2.BidRequest{
+					ID:     "1",
+					Imp:    []openrtb2.Imp{{ID: "1", Ext: json.RawMessage(`{"tid": "{{UUID}}"}`)}},
+					Source: &openrtb2.Source{},
+				},
+			},
+			generateRequestID:   false,
+			hasStoredBidRequest: false,
+			isAmp:               false,
+			expectRandSourceTID: false,
+			expectRandImpTID:    true,
+		},
+		{
+			description: "req imp.tid is set,  hasStoredBidRequest = true, generateRequestID = true, expect to be replaced by random value",
+			req: &openrtb_ext.RequestWrapper{
+				BidRequest: &openrtb2.BidRequest{
+					ID:     "1",
+					Imp:    []openrtb2.Imp{{ID: "1", Ext: json.RawMessage(`{"tid": "some-tid"}`)}},
+					Source: &openrtb2.Source{TID: "test-tid"},
+				},
+			},
+			generateRequestID:   true,
+			hasStoredBidRequest: true,
+			isAmp:               false,
+			expectRandSourceTID: false,
+			expectRandImpTID:    true,
+		},
+		{
+			description: "req imp.tid is set,  isAmp = true, generateRequestID = true, expect to be replaced by random value",
+			req: &openrtb_ext.RequestWrapper{
+				BidRequest: &openrtb2.BidRequest{
+					ID:     "1",
+					Imp:    []openrtb2.Imp{{ID: "1", Ext: json.RawMessage(`{"tid": "some-tid"}`)}},
+					Source: &openrtb2.Source{TID: "test-tid"},
 				},
 			},
-			expectRandTID: false,
-			expectedTID:   testTID,
+			generateRequestID:   true,
+			hasStoredBidRequest: false,
+			isAmp:               true,
+			expectRandSourceTID: false,
+			expectRandImpTID:    true,
+		},
+		{
+			description: "req imp.tid is set,  hasStoredBidRequest = true, generateRequestID = false, expect NOT to be replaced by random value",
+			req: &openrtb_ext.RequestWrapper{
+				BidRequest: &openrtb2.BidRequest{
+					ID:     "1",
+					Imp:    []openrtb2.Imp{{ID: "1", Ext: json.RawMessage(`{"tid": "some-tid"}`)}},
+					Source: &openrtb2.Source{TID: testTID},
+				},
+			},
+			generateRequestID:   false,
+			hasStoredBidRequest: true,
+			isAmp:               false,
+			expectRandSourceTID: false,
+			expectRandImpTID:    false,
+			expectImpTid:        &testTID,
 		},
 	}
 
 	for _, test := range testCases {
-		_ = validateAndFillSourceTID(test.req)
-		if test.expectRandTID {
+		_ = validateAndFillSourceTID(test.req, test.generateRequestID, test.hasStoredBidRequest, test.isAmp)
+		impWrapper := &openrtb_ext.ImpWrapper{}
+		impWrapper.Imp = &test.req.Imp[0]
+		ie, _ := impWrapper.GetImpExt()
+		impTID := ie.GetTid()
+		if test.expectRandSourceTID {
 			assert.NotEmpty(t, test.req.Source.TID, test.description)
-			assert.NotEqual(t, test.expectedTID, test.req.Source.TID, test.description)
-		} else {
-			assert.Equal(t, test.expectedTID, test.req.Source.TID, test.description)
+		} else if test.expectRandImpTID {
+			assert.NotEqual(t, testTID, impTID, test.description)
+			assert.NotEmpty(t, impTID, test.description)
+		} else if test.expectSourceTid != nil {
+			assert.Equal(t, test.req.Source.TID, *test.expectSourceTid, test.description)
+		} else if test.expectImpTid != nil {
+			assert.Equal(t, impTID, *test.expectImpTid, test.description)
 		}
 	}
 }
@@ -3002,7 +3144,7 @@ func TestEidPermissionsInvalid(t *testing.T) {
 		Ext: json.RawMessage(`{"prebid": {"data": {"eidpermissions": [{"source":"a", "bidders":[]}]} } }`),
 	}
 
-	errL := deps.validateRequest(&openrtb_ext.RequestWrapper{BidRequest: &req}, false, false, nil)
+	errL := deps.validateRequest(&openrtb_ext.RequestWrapper{BidRequest: &req}, false, false, nil, false)
 
 	expectedError := errors.New(`request.ext.prebid.data.eidpermissions[0] missing or empty required field: "bidders"`)
 	assert.ElementsMatch(t, errL, []error{expectedError})
@@ -4652,7 +4794,7 @@ func TestValidateStoredResp(t *testing.T) {
 	}
 
 	for _, test := range testCases {
-		errorList := deps.validateRequest(test.givenRequestWrapper, false, test.hasStoredAuctionResponses, test.storedBidResponses)
+		errorList := deps.validateRequest(test.givenRequestWrapper, false, test.hasStoredAuctionResponses, test.storedBidResponses, false)
 		assert.Equalf(t, test.expectedErrorList, errorList, "Error doesn't match: %s\n", test.description)
 	}
 }
diff --git a/endpoints/openrtb2/test_utils.go b/endpoints/openrtb2/test_utils.go
index cf803a34..2a59fc45 100644
--- a/endpoints/openrtb2/test_utils.go
+++ b/endpoints/openrtb2/test_utils.go
@@ -1555,7 +1555,7 @@ var entryPointHookUpdateWithErrors = hooks.HookWrapper[hookstage.Entrypoint]{
 			_ hookstage.ModuleInvocationContext,
 			payload hookstage.EntrypointPayload,
 		) (hookstage.HookResult[hookstage.EntrypointPayload], error) {
-			ch := &hookstage.ChangeSet[hookstage.EntrypointPayload]{}
+			ch := hookstage.ChangeSet[hookstage.EntrypointPayload]{}
 			ch.AddMutation(func(payload hookstage.EntrypointPayload) (hookstage.EntrypointPayload, error) {
 				payload.Request.Header.Add("foo", "bar")
 				return payload, nil
@@ -1577,7 +1577,7 @@ var entryPointHookUpdateWithErrorsAndWarnings = hooks.HookWrapper[hookstage.Entr
 			_ hookstage.ModuleInvocationContext,
 			payload hookstage.EntrypointPayload,
 		) (hookstage.HookResult[hookstage.EntrypointPayload], error) {
-			ch := &hookstage.ChangeSet[hookstage.EntrypointPayload]{}
+			ch := hookstage.ChangeSet[hookstage.EntrypointPayload]{}
 			ch.AddMutation(func(payload hookstage.EntrypointPayload) (hookstage.EntrypointPayload, error) {
 				params := payload.Request.URL.Query()
 				params.Add("foo", "baz")
@@ -1608,7 +1608,7 @@ var entryPointHookUpdate = hooks.HookWrapper[hookstage.Entrypoint]{
 				return result, nil
 			}
 
-			ch := &hookstage.ChangeSet[hookstage.EntrypointPayload]{}
+			ch := hookstage.ChangeSet[hookstage.EntrypointPayload]{}
 			ch.AddMutation(func(payload hookstage.EntrypointPayload) (hookstage.EntrypointPayload, error) {
 				body, err := jsonpatch.MergePatch(payload.Body, []byte(`{"tmax":50}`))
 				if err == nil {
diff --git a/exchange/exchange_test.go b/exchange/exchange_test.go
index d63fa6f6..cac87f5b 100644
--- a/exchange/exchange_test.go
+++ b/exchange/exchange_test.go
@@ -171,7 +171,7 @@ func TestCharacterEscape(t *testing.T) {
 	var errList []error
 
 	// 	4) Build bid response
-	bidResp, err := e.buildBidResponse(context.Background(), liveAdapters, adapterBids, bidRequest, adapterExtra, nil, nil, true, nil, errList)
+	bidResp, err := e.buildBidResponse(context.Background(), liveAdapters, adapterBids, bidRequest, adapterExtra, nil, nil, true, nil, "", errList)
 
 	// 	5) Assert we have no errors and one '&' character as we are supposed to
 	if err != nil {
@@ -1359,7 +1359,7 @@ func TestGetBidCacheInfoEndToEnd(t *testing.T) {
 	var errList []error
 
 	// 	4) Build bid response
-	bid_resp, err := e.buildBidResponse(context.Background(), liveAdapters, adapterBids, bidRequest, adapterExtra, auc, nil, true, nil, errList)
+	bid_resp, err := e.buildBidResponse(context.Background(), liveAdapters, adapterBids, bidRequest, adapterExtra, auc, nil, true, nil, "", errList)
 
 	// 	5) Assert we have no errors and the bid response we expected
 	assert.NoError(t, err, "[TestGetBidCacheInfo] buildBidResponse() threw an error")
@@ -1452,7 +1452,7 @@ func TestBidReturnsCreative(t *testing.T) {
 
 	//Run tests
 	for _, test := range testCases {
-		resultingBids, resultingErrs := e.makeBid(sampleBids, sampleAuction, test.inReturnCreative, nil)
+		resultingBids, resultingErrs := e.makeBid(sampleBids, sampleAuction, test.inReturnCreative, nil, nil, "", "")
 
 		assert.Equal(t, 0, len(resultingErrs), "%s. Test should not return errors \n", test.description)
 		assert.Equal(t, test.expectedCreativeMarkup, resultingBids[0].AdM, "%s. Ad markup string doesn't match expected \n", test.description)
@@ -1746,7 +1746,7 @@ func TestBidResponseCurrency(t *testing.T) {
 	}
 	// Run tests
 	for i := range testCases {
-		actualBidResp, err := e.buildBidResponse(context.Background(), liveAdapters, testCases[i].adapterBids, bidRequest, adapterExtra, nil, bidResponseExt, true, nil, errList)
+		actualBidResp, err := e.buildBidResponse(context.Background(), liveAdapters, testCases[i].adapterBids, bidRequest, adapterExtra, nil, bidResponseExt, true, nil, "", errList)
 		assert.NoError(t, err, fmt.Sprintf("[TEST_FAILED] e.buildBidResponse resturns error in test: %s Error message: %s \n", testCases[i].description, err))
 		assert.Equalf(t, testCases[i].expectedBidResponse, actualBidResp, fmt.Sprintf("[TEST_FAILED] Objects must be equal for test: %s \n Expected: >>%s<< \n Actual: >>%s<< ", testCases[i].description, testCases[i].expectedBidResponse.Ext, actualBidResp.Ext))
 	}
@@ -1816,7 +1816,7 @@ func TestBidResponseImpExtInfo(t *testing.T) {
 
 	expectedBidResponseExt := `{"origbidcpm":0,"prebid":{"type":"video","passthrough":{"imp_passthrough_val":1}},"storedrequestattributes":{"h":480,"mimes":["video/mp4"]}}`
 
-	actualBidResp, err := e.buildBidResponse(context.Background(), liveAdapters, adapterBids, bidRequest, nil, nil, nil, true, impExtInfo, errList)
+	actualBidResp, err := e.buildBidResponse(context.Background(), liveAdapters, adapterBids, bidRequest, nil, nil, nil, true, impExtInfo, "", errList)
 	assert.NoError(t, err, fmt.Sprintf("imp ext info was not passed through correctly: %s", err))
 
 	resBidExt := string(actualBidResp.SeatBid[0].Bid[0].Ext)
@@ -2179,7 +2179,7 @@ func runSpec(t *testing.T, filename string, spec *exchangeSpec) {
 	if spec.BidIDGenerator != nil {
 		*bidIdGenerator = *spec.BidIDGenerator
 	}
-	ex := newExchangeForTests(t, filename, spec.OutgoingRequests, aliases, privacyConfig, bidIdGenerator, spec.HostSChainFlag)
+	ex := newExchangeForTests(t, filename, spec.OutgoingRequests, aliases, privacyConfig, bidIdGenerator, spec.HostSChainFlag, spec.HostConfigBidValidation)
 	biddersInAuction := findBiddersInAuction(t, filename, &spec.IncomingRequest.OrtbRequest)
 	debugLog := &DebugLog{}
 	if spec.DebugLog != nil {
@@ -2197,12 +2197,22 @@ func runSpec(t *testing.T, filename string, spec *exchangeSpec) {
 		impExtInfoMap[impID] = ImpExtInfo{Passthrough: impPassthrough}
 	}
 
+	// Imp Setting for Bid Validation
+	if spec.HostConfigBidValidation.SecureMarkup == config.ValidationEnforce || spec.HostConfigBidValidation.SecureMarkup == config.ValidationWarn {
+		_, impID, err := getInfoFromImp(&openrtb_ext.RequestWrapper{BidRequest: &spec.IncomingRequest.OrtbRequest})
+		if err != nil {
+			t.Errorf("%s: Exchange returned an unexpected error. Got %s", filename, err.Error())
+		}
+		impExtInfoMap[impID] = ImpExtInfo{}
+	}
+
 	auctionRequest := AuctionRequest{
 		BidRequestWrapper: &openrtb_ext.RequestWrapper{BidRequest: &spec.IncomingRequest.OrtbRequest},
 		Account: config.Account{
 			ID:            "testaccount",
 			EventsEnabled: spec.EventsEnabled,
 			DebugAllow:    true,
+			Validations:   spec.AccountConfigBidValidation,
 		},
 		UserSyncs:     mockIdFetcher(spec.IncomingRequest.Usersyncs),
 		ImpExtInfoMap: impExtInfoMap,
@@ -2288,6 +2298,21 @@ func runSpec(t *testing.T, filename string, spec *exchangeSpec) {
 		}
 
 	}
+
+	if spec.BidValidationEnforcement == config.ValidationEnforce {
+		actualBidRespExt := &openrtb_ext.ExtBidResponse{}
+		expectedBidRespExt := &openrtb_ext.ExtBidResponse{}
+		if bid.Ext != nil {
+			if err := json.Unmarshal(bid.Ext, actualBidRespExt); err != nil {
+				assert.NoError(t, err, fmt.Sprintf("Error when unmarshalling: %s", err))
+			}
+		}
+		if err := json.Unmarshal(spec.Response.Ext, expectedBidRespExt); err != nil {
+			assert.NoError(t, err, fmt.Sprintf("Error when unmarshalling: %s", err))
+		}
+
+		assert.Equal(t, expectedBidRespExt.Errors, actualBidRespExt.Errors, "Oh no")
+	}
 }
 
 func findBiddersInAuction(t *testing.T, context string, req *openrtb2.BidRequest) []string {
@@ -2327,7 +2352,7 @@ func extractResponseTimes(t *testing.T, context string, bid *openrtb2.BidRespons
 	}
 }
 
-func newExchangeForTests(t *testing.T, filename string, expectations map[string]*bidderSpec, aliases map[string]string, privacyConfig config.Privacy, bidIDGenerator BidIDGenerator, hostSChainFlag bool) Exchange {
+func newExchangeForTests(t *testing.T, filename string, expectations map[string]*bidderSpec, aliases map[string]string, privacyConfig config.Privacy, bidIDGenerator BidIDGenerator, hostSChainFlag bool, hostBidValidation config.Validations) Exchange {
 	bidderAdapters := make(map[openrtb_ext.BidderName]AdaptedBidder, len(expectations))
 	bidderInfos := make(config.BidderInfos, len(expectations))
 	for _, bidderName := range openrtb_ext.CoreBidderNames() {
@@ -2394,22 +2419,23 @@ func newExchangeForTests(t *testing.T, filename string, expectations map[string]
 	}
 
 	return &exchange{
-		adapterMap:        bidderAdapters,
-		me:                metricsConf.NewMetricsEngine(&config.Configuration{}, openrtb_ext.CoreBidderNames(), nil, nil),
-		cache:             &wellBehavedCache{},
-		cacheTime:         0,
-		currencyConverter: currency.NewRateConverter(&http.Client{}, "", time.Duration(0)),
-		gdprDefaultValue:  gdprDefaultValue,
-		gdprPermsBuilder:  gdprPermsBuilder,
-		tcf2ConfigBuilder: tcf2ConfigBuilder,
-		privacyConfig:     privacyConfig,
-		categoriesFetcher: categoriesFetcher,
-		bidderInfo:        bidderInfos,
-		bidderToSyncerKey: bidderToSyncerKey,
-		externalURL:       "http://localhost",
-		bidIDGenerator:    bidIDGenerator,
-		hostSChainNode:    hostSChainNode,
-		server:            config.Server{ExternalUrl: "http://hosturl.com", GvlID: 1, DataCenter: "Datacenter"},
+		adapterMap:               bidderAdapters,
+		me:                       metricsConf.NewMetricsEngine(&config.Configuration{}, openrtb_ext.CoreBidderNames(), nil, nil),
+		cache:                    &wellBehavedCache{},
+		cacheTime:                0,
+		currencyConverter:        currency.NewRateConverter(&http.Client{}, "", time.Duration(0)),
+		gdprDefaultValue:         gdprDefaultValue,
+		gdprPermsBuilder:         gdprPermsBuilder,
+		tcf2ConfigBuilder:        tcf2ConfigBuilder,
+		privacyConfig:            privacyConfig,
+		categoriesFetcher:        categoriesFetcher,
+		bidderInfo:               bidderInfos,
+		bidderToSyncerKey:        bidderToSyncerKey,
+		externalURL:              "http://localhost",
+		bidIDGenerator:           bidIDGenerator,
+		hostSChainNode:           hostSChainNode,
+		server:                   config.Server{ExternalUrl: "http://hosturl.com", GvlID: 1, DataCenter: "Datacenter"},
+		bidValidationEnforcement: hostBidValidation,
 	}
 }
 
@@ -4212,6 +4238,238 @@ func TestCallSignHeader(t *testing.T) {
 
 }
 
+func TestValidateBannerCreativeSize(t *testing.T) {
+	exchange := exchange{bidValidationEnforcement: config.Validations{MaxCreativeWidth: 100, MaxCreativeHeight: 100},
+		me: metricsConf.NewMetricsEngine(&config.Configuration{}, openrtb_ext.CoreBidderNames(), nil, nil),
+	}
+	testCases := []struct {
+		description                 string
+		givenBid                    *entities.PbsOrtbBid
+		givenBidResponseExt         *openrtb_ext.ExtBidResponse
+		givenBidderName             string
+		givenPubID                  string
+		expectedBannerCreativeValid bool
+	}{
+		{
+			description:                 "The dimensions are invalid, both values bigger than the max",
+			givenBid:                    &entities.PbsOrtbBid{Bid: &openrtb2.Bid{W: 200, H: 200}},
+			givenBidResponseExt:         &openrtb_ext.ExtBidResponse{Errors: make(map[openrtb_ext.BidderName][]openrtb_ext.ExtBidderMessage)},
+			givenBidderName:             "bidder",
+			givenPubID:                  "1",
+			expectedBannerCreativeValid: false,
+		},
+		{
+			description:                 "The width is invalid, height is valid, the dimensions as a whole are invalid",
+			givenBid:                    &entities.PbsOrtbBid{Bid: &openrtb2.Bid{W: 200, H: 50}},
+			givenBidResponseExt:         &openrtb_ext.ExtBidResponse{Errors: make(map[openrtb_ext.BidderName][]openrtb_ext.ExtBidderMessage)},
+			givenBidderName:             "bidder",
+			givenPubID:                  "1",
+			expectedBannerCreativeValid: false,
+		},
+		{
+			description:                 "The width is valid, height is invalid, the dimensions as a whole are invalid",
+			givenBid:                    &entities.PbsOrtbBid{Bid: &openrtb2.Bid{W: 50, H: 200}},
+			givenBidResponseExt:         &openrtb_ext.ExtBidResponse{Errors: make(map[openrtb_ext.BidderName][]openrtb_ext.ExtBidderMessage)},
+			givenBidderName:             "bidder",
+			givenPubID:                  "1",
+			expectedBannerCreativeValid: false,
+		},
+		{
+			description:                 "Both width and height are valid, the dimensions are valid",
+			givenBid:                    &entities.PbsOrtbBid{Bid: &openrtb2.Bid{W: 50, H: 50}},
+			givenBidResponseExt:         &openrtb_ext.ExtBidResponse{Errors: make(map[openrtb_ext.BidderName][]openrtb_ext.ExtBidderMessage)},
+			givenBidderName:             "bidder",
+			givenPubID:                  "1",
+			expectedBannerCreativeValid: true,
+		},
+	}
+	for _, test := range testCases {
+		acutalBannerCreativeValid := exchange.validateBannerCreativeSize(test.givenBid, test.givenBidResponseExt, openrtb_ext.BidderName(test.givenBidderName), test.givenPubID, "enforce")
+		assert.Equal(t, test.expectedBannerCreativeValid, acutalBannerCreativeValid)
+	}
+}
+
+func TestValidateBidAdM(t *testing.T) {
+	exchange := exchange{bidValidationEnforcement: config.Validations{MaxCreativeWidth: 100, MaxCreativeHeight: 100},
+		me: metricsConf.NewMetricsEngine(&config.Configuration{}, openrtb_ext.CoreBidderNames(), nil, nil),
+	}
+	testCases := []struct {
+		description         string
+		givenBid            *entities.PbsOrtbBid
+		givenBidResponseExt *openrtb_ext.ExtBidResponse
+		givenBidderName     string
+		givenPubID          string
+		expectedBidAdMValid bool
+	}{
+		{
+			description:         "The adm of the bid contains insecure string and no secure string, adm is invalid",
+			givenBid:            &entities.PbsOrtbBid{Bid: &openrtb2.Bid{AdM: "http://domain.com/invalid"}},
+			givenBidResponseExt: &openrtb_ext.ExtBidResponse{Errors: make(map[openrtb_ext.BidderName][]openrtb_ext.ExtBidderMessage)},
+			givenBidderName:     "bidder",
+			givenPubID:          "1",
+			expectedBidAdMValid: false,
+		},
+		{
+			description:         "The adm has both an insecure and secure string defined and therefore the adm is valid",
+			givenBid:            &entities.PbsOrtbBid{Bid: &openrtb2.Bid{AdM: "http://www.foo.com https://www.bar.com"}},
+			givenBidResponseExt: &openrtb_ext.ExtBidResponse{Errors: make(map[openrtb_ext.BidderName][]openrtb_ext.ExtBidderMessage)},
+			givenBidderName:     "bidder",
+			givenPubID:          "1",
+			expectedBidAdMValid: true,
+		},
+		{
+			description:         "The adm has both an insecure and secure string defined and therefore the adm is valid",
+			givenBid:            &entities.PbsOrtbBid{Bid: &openrtb2.Bid{AdM: "http%3A//www.foo.com https%3A//www.bar.com"}},
+			givenBidResponseExt: &openrtb_ext.ExtBidResponse{Errors: make(map[openrtb_ext.BidderName][]openrtb_ext.ExtBidderMessage)},
+			givenBidderName:     "bidder",
+			givenPubID:          "1",
+			expectedBidAdMValid: true,
+		},
+		{
+			description:         "The adm of the bid are valid with a secure string",
+			givenBid:            &entities.PbsOrtbBid{Bid: &openrtb2.Bid{AdM: "https://domain.com/valid"}},
+			givenBidResponseExt: &openrtb_ext.ExtBidResponse{Errors: make(map[openrtb_ext.BidderName][]openrtb_ext.ExtBidderMessage)},
+			givenBidderName:     "bidder",
+			givenPubID:          "1",
+			expectedBidAdMValid: true,
+		},
+	}
+	for _, test := range testCases {
+		actualBidAdMValid := exchange.validateBidAdM(test.givenBid, test.givenBidResponseExt, openrtb_ext.BidderName(test.givenBidderName), test.givenPubID, "enforce")
+		assert.Equal(t, test.expectedBidAdMValid, actualBidAdMValid)
+
+	}
+}
+
+func TestMakeBidWithValidation(t *testing.T) {
+	sampleAd := "<?xml version=\"1.0\" encoding=\"UTF-8\"?><VAST ...></VAST>"
+	sampleOpenrtbBid := &openrtb2.Bid{ID: "some-bid-id", AdM: sampleAd}
+
+	// Define test cases
+	testCases := []struct {
+		description       string
+		givenValidations  config.Validations
+		givenBids         []*entities.PbsOrtbBid
+		expectedNumOfBids int
+	}{
+		{
+			description:       "Validation is enforced, and one bid out of the two is invalid based on dimensions",
+			givenValidations:  config.Validations{BannerCreativeMaxSize: config.ValidationEnforce, MaxCreativeWidth: 100, MaxCreativeHeight: 100},
+			givenBids:         []*entities.PbsOrtbBid{{Bid: &openrtb2.Bid{W: 200, H: 200}, BidType: openrtb_ext.BidTypeBanner}, {Bid: &openrtb2.Bid{W: 50, H: 50}, BidType: openrtb_ext.BidTypeBanner}},
+			expectedNumOfBids: 1,
+		},
+		{
+			description:       "Validation is warned, so no bids should be removed (Validating CreativeMaxSize) ",
+			givenValidations:  config.Validations{BannerCreativeMaxSize: config.ValidationWarn, MaxCreativeWidth: 100, MaxCreativeHeight: 100},
+			givenBids:         []*entities.PbsOrtbBid{{Bid: &openrtb2.Bid{W: 200, H: 200}, BidType: openrtb_ext.BidTypeBanner}, {Bid: &openrtb2.Bid{W: 50, H: 50}, BidType: openrtb_ext.BidTypeBanner}},
+			expectedNumOfBids: 2,
+		},
+		{
+			description:       "Validation is enforced, and one bid out of the two is invalid based on AdM",
+			givenValidations:  config.Validations{SecureMarkup: config.ValidationEnforce},
+			givenBids:         []*entities.PbsOrtbBid{{Bid: &openrtb2.Bid{AdM: "http://domain.com/invalid", ImpID: "1"}, BidType: openrtb_ext.BidTypeBanner}, {Bid: &openrtb2.Bid{AdM: "https://domain.com/valid", ImpID: "2"}, BidType: openrtb_ext.BidTypeBanner}},
+			expectedNumOfBids: 1,
+		},
+		{
+			description:       "Validation is warned so no bids should be removed (Validating SecureMarkup)",
+			givenValidations:  config.Validations{SecureMarkup: config.ValidationWarn},
+			givenBids:         []*entities.PbsOrtbBid{{Bid: &openrtb2.Bid{AdM: "http://domain.com/invalid", ImpID: "1"}, BidType: openrtb_ext.BidTypeBanner}, {Bid: &openrtb2.Bid{AdM: "https://domain.com/valid", ImpID: "2"}, BidType: openrtb_ext.BidTypeBanner}},
+			expectedNumOfBids: 2,
+		},
+		{
+			description:       "Adm validation is skipped, creative size validation is enforced, one Adm is invalid, but because we skip, no bids should be removed",
+			givenValidations:  config.Validations{SecureMarkup: config.ValidationSkip, BannerCreativeMaxSize: config.ValidationEnforce},
+			givenBids:         []*entities.PbsOrtbBid{{Bid: &openrtb2.Bid{AdM: "http://domain.com/invalid"}, BidType: openrtb_ext.BidTypeBanner}, {Bid: &openrtb2.Bid{AdM: "https://domain.com/valid"}, BidType: openrtb_ext.BidTypeBanner}},
+			expectedNumOfBids: 2,
+		},
+		{
+			description:       "Creative Size Validation is skipped, Adm Validation is enforced, one Creative Size is invalid, but because we skip, no bids should be removed",
+			givenValidations:  config.Validations{BannerCreativeMaxSize: config.ValidationWarn, MaxCreativeWidth: 100, MaxCreativeHeight: 100},
+			givenBids:         []*entities.PbsOrtbBid{{Bid: &openrtb2.Bid{W: 200, H: 200}, BidType: openrtb_ext.BidTypeBanner}, {Bid: &openrtb2.Bid{W: 50, H: 50}, BidType: openrtb_ext.BidTypeBanner}},
+			expectedNumOfBids: 2,
+		},
+	}
+
+	// Test set up
+	sampleAuction := &auction{cacheIds: map[*openrtb2.Bid]string{sampleOpenrtbBid: "CACHE_UUID_1234"}}
+
+	noBidHandler := func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(204) }
+	server := httptest.NewServer(http.HandlerFunc(noBidHandler))
+	defer server.Close()
+
+	bidderImpl := &goodSingleBidder{
+		httpRequest: &adapters.RequestData{
+			Method:  "POST",
+			Uri:     server.URL,
+			Body:    []byte("{\"key\":\"val\"}"),
+			Headers: http.Header{},
+		},
+		bidResponse: &adapters.BidderResponse{},
+	}
+	e := new(exchange)
+	e.adapterMap = map[openrtb_ext.BidderName]AdaptedBidder{
+		openrtb_ext.BidderAppnexus: AdaptBidder(bidderImpl, server.Client(), &config.Configuration{}, &metricsConfig.NilMetricsEngine{}, openrtb_ext.BidderAppnexus, nil, ""),
+	}
+	e.cache = &wellBehavedCache{}
+	e.me = &metricsConf.NilMetricsEngine{}
+
+	e.currencyConverter = currency.NewRateConverter(&http.Client{}, "", time.Duration(0))
+
+	bidExtResponse := &openrtb_ext.ExtBidResponse{Errors: make(map[openrtb_ext.BidderName][]openrtb_ext.ExtBidderMessage)}
+
+	ImpExtInfoMap := make(map[string]ImpExtInfo)
+	ImpExtInfoMap["1"] = ImpExtInfo{}
+	ImpExtInfoMap["2"] = ImpExtInfo{}
+
+	//Run tests
+	for _, test := range testCases {
+		e.bidValidationEnforcement = test.givenValidations
+		sampleBids := test.givenBids
+		resultingBids, resultingErrs := e.makeBid(sampleBids, sampleAuction, true, ImpExtInfoMap, bidExtResponse, "", "")
+
+		assert.Equal(t, 0, len(resultingErrs), "%s. Test should not return errors \n", test.description)
+		assert.Equal(t, test.expectedNumOfBids, len(resultingBids), "%s. Test returns more valid bids than expected\n", test.description)
+	}
+}
+
+func TestSetBidValidationStatus(t *testing.T) {
+	testCases := []struct {
+		description  string
+		givenHost    config.Validations
+		givenAccount config.Validations
+		expected     config.Validations
+	}{
+		{
+			description:  "Host configuration is different than account, account setting should be preferred (enforce)",
+			givenHost:    config.Validations{BannerCreativeMaxSize: config.ValidationSkip, SecureMarkup: config.ValidationSkip},
+			givenAccount: config.Validations{BannerCreativeMaxSize: config.ValidationEnforce, SecureMarkup: config.ValidationEnforce},
+			expected:     config.Validations{BannerCreativeMaxSize: config.ValidationEnforce, SecureMarkup: config.ValidationSkip},
+		},
+		{
+			description:  "Host configuration is different than account, account setting should be preferred (warn)",
+			givenHost:    config.Validations{BannerCreativeMaxSize: config.ValidationEnforce, SecureMarkup: config.ValidationEnforce},
+			givenAccount: config.Validations{BannerCreativeMaxSize: config.ValidationWarn, SecureMarkup: config.ValidationWarn},
+			expected:     config.Validations{BannerCreativeMaxSize: config.ValidationWarn, SecureMarkup: config.ValidationEnforce},
+		},
+		{
+			description:  "Host configuration is different than account, account setting should be preferred (skip)",
+			givenHost:    config.Validations{BannerCreativeMaxSize: config.ValidationWarn, SecureMarkup: config.ValidationWarn},
+			givenAccount: config.Validations{BannerCreativeMaxSize: config.ValidationSkip, SecureMarkup: config.ValidationSkip},
+			expected:     config.Validations{BannerCreativeMaxSize: config.ValidationSkip, SecureMarkup: config.ValidationWarn},
+		},
+		{
+			description:  "No account confiugration given, host confg should be preferred",
+			givenHost:    config.Validations{BannerCreativeMaxSize: config.ValidationSkip, SecureMarkup: config.ValidationSkip},
+			givenAccount: config.Validations{},
+			expected:     config.Validations{BannerCreativeMaxSize: config.ValidationSkip, SecureMarkup: config.ValidationSkip},
+		},
+	}
+	for _, test := range testCases {
+		test.givenHost.SetBannerCreativeMaxSize(test.givenAccount)
+		assert.Equal(t, test.expected, test.givenHost)
+	}
+}
+
 /*
 TestOverrideConfigAlternateBidderCodesWithRequestValues makes sure that the correct alternabiddercodes list is forwarded to the adapters and only the approved bids are returned in auction response.
 
@@ -4402,20 +4660,23 @@ func (ms *MockSigner) Sign(destinationURL string, body []byte) (string, error) {
 }
 
 type exchangeSpec struct {
-	GDPREnabled       bool                   `json:"gdpr_enabled"`
-	IncomingRequest   exchangeRequest        `json:"incomingRequest"`
-	OutgoingRequests  map[string]*bidderSpec `json:"outgoingRequests"`
-	Response          exchangeResponse       `json:"response,omitempty"`
-	EnforceCCPA       bool                   `json:"enforceCcpa"`
-	EnforceLMT        bool                   `json:"enforceLmt"`
-	AssumeGDPRApplies bool                   `json:"assume_gdpr_applies"`
-	DebugLog          *DebugLog              `json:"debuglog,omitempty"`
-	EventsEnabled     bool                   `json:"events_enabled,omitempty"`
-	StartTime         int64                  `json:"start_time_ms,omitempty"`
-	BidIDGenerator    *mockBidIDGenerator    `json:"bidIDGenerator,omitempty"`
-	RequestType       *metrics.RequestType   `json:"requestType,omitempty"`
-	PassthroughFlag   bool                   `json:"passthrough_flag,omitempty"`
-	HostSChainFlag    bool                   `json:"host_schain_flag,omitempty"`
+	GDPREnabled                bool                   `json:"gdpr_enabled"`
+	IncomingRequest            exchangeRequest        `json:"incomingRequest"`
+	OutgoingRequests           map[string]*bidderSpec `json:"outgoingRequests"`
+	Response                   exchangeResponse       `json:"response,omitempty"`
+	EnforceCCPA                bool                   `json:"enforceCcpa"`
+	EnforceLMT                 bool                   `json:"enforceLmt"`
+	AssumeGDPRApplies          bool                   `json:"assume_gdpr_applies"`
+	DebugLog                   *DebugLog              `json:"debuglog,omitempty"`
+	EventsEnabled              bool                   `json:"events_enabled,omitempty"`
+	StartTime                  int64                  `json:"start_time_ms,omitempty"`
+	BidIDGenerator             *mockBidIDGenerator    `json:"bidIDGenerator,omitempty"`
+	RequestType                *metrics.RequestType   `json:"requestType,omitempty"`
+	PassthroughFlag            bool                   `json:"passthrough_flag,omitempty"`
+	HostSChainFlag             bool                   `json:"host_schain_flag,omitempty"`
+	BidValidationEnforcement   string                 `json:"bid_validation_flag,omitempty"`
+	HostConfigBidValidation    config.Validations     `json:"host_bid_validations"`
+	AccountConfigBidValidation config.Validations     `json:"account_bid_validations"`
 }
 
 type exchangeRequest struct {
@@ -4824,7 +5085,7 @@ type mockUpdateBidRequestHook struct{}
 
 func (e mockUpdateBidRequestHook) HandleBidderRequestHook(_ context.Context, mctx hookstage.ModuleInvocationContext, _ hookstage.BidderRequestPayload) (hookstage.HookResult[hookstage.BidderRequestPayload], error) {
 	time.Sleep(50 * time.Millisecond)
-	c := &hookstage.ChangeSet[hookstage.BidderRequestPayload]{}
+	c := hookstage.ChangeSet[hookstage.BidderRequestPayload]{}
 	c.AddMutation(
 		func(payload hookstage.BidderRequestPayload) (hookstage.BidderRequestPayload, error) {
 			payload.BidRequest.Site.Name = "test"
diff --git a/exchange/exchangetest/bid_response_validation_enforce_creative.json b/exchange/exchangetest/bid_response_validation_enforce_creative.json
new file mode 100644
index 00000000..0c8f1467
--- /dev/null
+++ b/exchange/exchangetest/bid_response_validation_enforce_creative.json
@@ -0,0 +1,189 @@
+{
+    "host_bid_validations": {
+        "banner_creative_max_size": "skip",
+        "secure_markup": "skip",
+        "max_creative_width": 100,
+        "max_creative_height": 100
+    },
+    "account_bid_validations": {
+        "banner_creative_max_size": "enforce"
+    },
+    "incomingRequest": {
+        "ortbRequest": {
+            "id": "some-request-id",
+            "site": {
+                "page": "test.somepage.com"
+            },
+            "imp": [
+                {
+                    "id": "some-imp-id",
+                    "banner": {
+                        "format": [
+                            {
+                                "w": 600,
+                                "h": 500
+                            },
+                            {
+                                "w": 300,
+                                "h": 600
+                            }
+                        ]
+                    },
+                    "ext": {
+                        "prebid": {
+                            "bidder": {
+                                "appnexus": {
+                                    "placementId": 1
+                                },
+                                "rubicon": {
+                                    "accountId": 1,
+                                    "siteId": 2,
+                                    "zoneId": 3
+                                }
+                            }
+                        }
+                    }
+                }
+            ]
+        }
+    },
+    "outgoingRequests": {
+        "appnexus": {
+            "expectRequest": {
+                "ortbRequest": {
+                    "id": "some-request-id",
+                    "site": {
+                        "page": "test.somepage.com"
+                    },
+                    "imp": [
+                        {
+                            "id": "some-imp-id",
+                            "banner": {
+                                "format": [
+                                    {
+                                        "w": 600,
+                                        "h": 500
+                                    },
+                                    {
+                                        "w": 300,
+                                        "h": 600
+                                    }
+                                ]
+                            },
+                            "ext": {
+                                "bidder": {
+                                    "placementId": 1
+                                }
+                            }
+                        }
+                    ]
+                }
+            },
+            "mockResponse": {
+                "pbsSeatBids": [
+                    {
+                        "pbsBids": [
+                            {
+                                "ortbBid": {
+                                    "id": "apn-bid",
+                                    "impid": "some-imp-id",
+                                    "price": 0.3,
+                                    "w": 200,
+                                    "h": 500,
+                                    "crid": "creative-1"
+                                },
+                                "bidType": "banner"
+                            }
+                        ],
+                        "seat": "appnexus"
+                    }
+                ]
+            }
+        },
+        "rubicon": {
+            "expectRequest": {
+                "ortbRequest": {
+                    "id": "some-request-id",
+                    "site": {
+                        "page": "test.somepage.com"
+                    },
+                    "imp": [
+                        {
+                            "id": "some-imp-id",
+                            "banner": {
+                                "format": [
+                                    {
+                                        "w": 600,
+                                        "h": 500
+                                    },
+                                    {
+                                        "w": 300,
+                                        "h": 600
+                                    }
+                                ]
+                            },
+                            "ext": {
+                                "bidder": {
+                                    "accountId": 1,
+                                    "siteId": 2,
+                                    "zoneId": 3
+                                }
+                            }
+                        }
+                    ]
+                }
+            },
+            "mockResponse": {
+                "pbsSeatBids": [
+                    {
+                        "pbsBids": [
+                            {
+                                "ortbBid": {
+                                    "id": "rubi-bid",
+                                    "impid": "some-imp-id",
+                                    "price": 0.4,
+                                    "w": 200,
+                                    "h": 500,
+                                    "crid": "creative-2"
+                                },
+                                "bidType": "banner"
+                            }
+                        ],
+                        "seat": "rubicon"
+                    }
+                ]
+            }
+        }
+    },
+    "response": {
+        "bids": {
+            "id": "some-request-id",
+            "seatbid": [
+                {
+                    "seat": "appnexus",
+                    "bid": []
+                },
+                {
+                    "seat": "rubicon",
+                    "bid": []
+                }
+            ]
+        },
+        "ext": {
+            "errors": {
+                "appnexus": [
+                    {
+                        "code": 4,
+                        "message": "bidResponse rejected: size WxH"
+                    }
+                ],
+                "rubicon": [
+                    {
+                        "code": 4,
+                        "message": "bidResponse rejected: size WxH"
+                    }
+                ]
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/exchange/exchangetest/bid_response_validation_enforce_one_bid_rejected.json b/exchange/exchangetest/bid_response_validation_enforce_one_bid_rejected.json
new file mode 100644
index 00000000..ea48f3a9
--- /dev/null
+++ b/exchange/exchangetest/bid_response_validation_enforce_one_bid_rejected.json
@@ -0,0 +1,195 @@
+{
+    "host_bid_validations": {
+        "banner_creative_max_size": "enforce",
+        "secure_markup": "skip",
+        "max_creative_width": 100,
+        "max_creative_height": 100
+    },
+    "incomingRequest": {
+        "ortbRequest": {
+            "id": "some-request-id",
+            "site": {
+                "page": "test.somepage.com"
+            },
+            "imp": [
+                {
+                    "id": "some-imp-id",
+                    "banner": {
+                        "format": [
+                            {
+                                "w": 600,
+                                "h": 500
+                            },
+                            {
+                                "w": 300,
+                                "h": 600
+                            }
+                        ]
+                    },
+                    "ext": {
+                        "prebid": {
+                            "bidder": {
+                                "appnexus": {
+                                    "placementId": 1
+                                },
+                                "rubicon": {
+                                    "accountId": 1,
+                                    "siteId": 2,
+                                    "zoneId": 3
+                                }
+                            }
+                        }
+                    }
+                }
+            ]
+        }
+    },
+    "outgoingRequests": {
+        "appnexus": {
+            "expectRequest": {
+                "ortbRequest": {
+                    "id": "some-request-id",
+                    "site": {
+                        "page": "test.somepage.com"
+                    },
+                    "imp": [
+                        {
+                            "id": "some-imp-id",
+                            "banner": {
+                                "format": [
+                                    {
+                                        "w": 600,
+                                        "h": 500
+                                    },
+                                    {
+                                        "w": 300,
+                                        "h": 600
+                                    }
+                                ]
+                            },
+                            "ext": {
+                                "bidder": {
+                                    "placementId": 1
+                                }
+                            }
+                        }
+                    ]
+                }
+            },
+            "mockResponse": {
+                "pbsSeatBids": [
+                    {
+                        "pbsBids": [
+                            {
+                                "ortbBid": {
+                                    "id": "apn-bid",
+                                    "impid": "some-imp-id",
+                                    "price": 0.3,
+                                    "w": 200,
+                                    "h": 500,
+                                    "crid": "creative-1"
+                                },
+                                "bidType": "banner"
+                            }
+                        ],
+                        "seat": "appnexus"
+                    }
+                ]
+            }
+        },
+        "rubicon": {
+            "expectRequest": {
+                "ortbRequest": {
+                    "id": "some-request-id",
+                    "site": {
+                        "page": "test.somepage.com"
+                    },
+                    "imp": [
+                        {
+                            "id": "some-imp-id",
+                            "banner": {
+                                "format": [
+                                    {
+                                        "w": 600,
+                                        "h": 500
+                                    },
+                                    {
+                                        "w": 300,
+                                        "h": 600
+                                    }
+                                ]
+                            },
+                            "ext": {
+                                "bidder": {
+                                    "accountId": 1,
+                                    "siteId": 2,
+                                    "zoneId": 3
+                                }
+                            }
+                        }
+                    ]
+                }
+            },
+            "mockResponse": {
+                "pbsSeatBids": [
+                    {
+                        "pbsBids": [
+                            {
+                                "ortbBid": {
+                                    "id": "rubi-bid",
+                                    "impid": "some-imp-id",
+                                    "price": 0.4,
+                                    "w": 20,
+                                    "h": 50,
+                                    "crid": "creative-2"
+                                },
+                                "bidType": "banner"
+                            }
+                        ],
+                        "seat": "rubicon"
+                    }
+                ]
+            }
+        }
+    },
+    "response": {
+        "bids": {
+            "id": "some-request-id",
+            "seatbid": [
+                {
+                    "seat": "appnexus",
+                    "bid": []
+                },
+                {
+                    "seat": "rubicon",
+                    "bid": [
+                        {
+                            "id": "rubi-bid",
+                            "impid": "some-imp-id",
+                            "price": 0.4,
+                            "w": 20,
+                            "h": 50,
+                            "crid": "creative-2",
+                            "ext": {
+                                "origbidcpm": 0.4,
+                                "prebid": {
+                                    "type": "banner"
+                                }
+                            }
+                        }
+                    ]
+                }
+            ]
+        },
+        "ext": {
+            "errors": {
+                "appnexus": [
+                    {
+                        "code": 4,
+                        "message": "bidResponse rejected: size WxH"
+                    }
+                ]
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/exchange/exchangetest/bid_response_validation_enforce_secure.json b/exchange/exchangetest/bid_response_validation_enforce_secure.json
new file mode 100644
index 00000000..e036f82a
--- /dev/null
+++ b/exchange/exchangetest/bid_response_validation_enforce_secure.json
@@ -0,0 +1,188 @@
+{
+    "host_bid_validations": {
+        "banner_creative_max_size": "skip",
+        "secure_markup": "enforce",
+        "max_creative_width": 100,
+        "max_creative_height": 100
+    },
+    "incomingRequest": {
+        "ortbRequest": {
+            "id": "some-request-id",
+            "site": {
+                "page": "test.somepage.com"
+            },
+            "imp": [
+                {
+                    "id": "some-imp-id",
+                    "banner": {
+                        "format": [
+                            {
+                                "w": 600,
+                                "h": 500
+                            },
+                            {
+                                "w": 300,
+                                "h": 600
+                            }
+                        ]
+                    },
+                    "ext": {
+                        "prebid": {
+                            "bidder": {
+                                "appnexus": {
+                                    "placementId": 1
+                                },
+                                "rubicon": {
+                                    "accountId": 1,
+                                    "siteId": 2,
+                                    "zoneId": 3
+                                }
+                            }
+                        }
+                    }
+                }
+            ]
+        }
+    },
+    "outgoingRequests": {
+        "appnexus": {
+            "expectRequest": {
+                "ortbRequest": {
+                    "id": "some-request-id",
+                    "site": {
+                        "page": "test.somepage.com"
+                    },
+                    "imp": [
+                        {
+                            "id": "some-imp-id",
+                            "banner": {
+                                "format": [
+                                    {
+                                        "w": 600,
+                                        "h": 500
+                                    },
+                                    {
+                                        "w": 300,
+                                        "h": 600
+                                    }
+                                ]
+                            },
+                            "ext": {
+                                "bidder": {
+                                    "placementId": 1
+                                }
+                            }
+                        }
+                    ]
+                }
+            },
+            "mockResponse": {
+                "pbsSeatBids": [
+                    {
+                        "pbsBids": [
+                            {
+                                "ortbBid": {
+                                    "id": "apn-bid",
+                                    "impid": "some-imp-id",
+                                    "price": 0.3,
+                                    "w": 20,
+                                    "h": 50,
+                                    "adm": "http:",
+                                    "crid": "creative-1"
+                                },
+                                "bidType": "banner"
+                            }
+                        ],
+                        "seat": "appnexus"
+                    }
+                ]
+            }
+        },
+        "rubicon": {
+            "expectRequest": {
+                "ortbRequest": {
+                    "id": "some-request-id",
+                    "site": {
+                        "page": "test.somepage.com"
+                    },
+                    "imp": [
+                        {
+                            "id": "some-imp-id",
+                            "banner": {
+                                "format": [
+                                    {
+                                        "w": 600,
+                                        "h": 500
+                                    },
+                                    {
+                                        "w": 300,
+                                        "h": 600
+                                    }
+                                ]
+                            },
+                            "ext": {
+                                "bidder": {
+                                    "accountId": 1,
+                                    "siteId": 2,
+                                    "zoneId": 3
+                                }
+                            }
+                        }
+                    ]
+                }
+            },
+            "mockResponse": {
+                "pbsSeatBids": [
+                    {
+                        "pbsBids": [
+                            {
+                                "ortbBid": {
+                                    "id": "rubi-bid",
+                                    "impid": "some-imp-id",
+                                    "price": 0.4,
+                                    "w": 20,
+                                    "h": 50,
+                                    "adm": "http:",
+                                    "crid": "creative-2"
+                                },
+                                "bidType": "banner"
+                            }
+                        ],
+                        "seat": "rubicon"
+                    }
+                ]
+            }
+        }
+    },
+    "response": {
+        "bids": {
+            "id": "some-request-id",
+            "seatbid": [
+                {
+                    "seat": "appnexus",
+                    "bid": []
+                },
+                {
+                    "seat": "rubicon",
+                    "bid": []
+                }
+            ]
+        },
+        "ext": {
+            "errors": {
+                "appnexus": [
+                    {
+                        "code": 4,
+                        "message": "bidResponse rejected: insecure creative in secure context"
+                    }
+                ],
+                "rubicon": [
+                    {
+                        "code": 4,
+                        "message": "bidResponse rejected: insecure creative in secure context"
+                    }
+                ]
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/exchange/exchangetest/bid_response_validation_warn_creative.json b/exchange/exchangetest/bid_response_validation_warn_creative.json
new file mode 100644
index 00000000..a170eac7
--- /dev/null
+++ b/exchange/exchangetest/bid_response_validation_warn_creative.json
@@ -0,0 +1,216 @@
+{
+    "host_bid_validations": {
+        "banner_creative_max_size": "warn",
+        "secure_markup": "skip",
+        "max_creative_width": 100,
+        "max_creative_height": 100
+    },
+    "incomingRequest": {
+        "ortbRequest": {
+            "id": "some-request-id",
+            "site": {
+                "page": "test.somepage.com"
+            },
+            "imp": [
+                {
+                    "id": "some-imp-id",
+                    "banner": {
+                        "format": [
+                            {
+                                "w": 600,
+                                "h": 500
+                            },
+                            {
+                                "w": 300,
+                                "h": 600
+                            }
+                        ]
+                    },
+                    "ext": {
+                        "prebid": {
+                            "bidder": {
+                                "appnexus": {
+                                    "placementId": 1
+                                },
+                                "rubicon": {
+                                    "accountId": 1,
+                                    "siteId": 2,
+                                    "zoneId": 3
+                                }
+                            }
+                        }
+                    }
+                }
+            ]
+        }
+    },
+    "outgoingRequests": {
+        "appnexus": {
+            "expectRequest": {
+                "ortbRequest": {
+                    "id": "some-request-id",
+                    "site": {
+                        "page": "test.somepage.com"
+                    },
+                    "imp": [
+                        {
+                            "id": "some-imp-id",
+                            "banner": {
+                                "format": [
+                                    {
+                                        "w": 600,
+                                        "h": 500
+                                    },
+                                    {
+                                        "w": 300,
+                                        "h": 600
+                                    }
+                                ]
+                            },
+                            "ext": {
+                                "bidder": {
+                                    "placementId": 1
+                                }
+                            }
+                        }
+                    ]
+                }
+            },
+            "mockResponse": {
+                "pbsSeatBids": [
+                    {
+                        "pbsBids": [
+                            {
+                                "ortbBid": {
+                                    "id": "apn-bid",
+                                    "impid": "some-imp-id",
+                                    "price": 0.3,
+                                    "w": 200,
+                                    "h": 500,
+                                    "crid": "creative-1"
+                                },
+                                "bidType": "banner"
+                            }
+                        ],
+                        "seat": "appnexus"
+                    }
+                ]
+            }
+        },
+        "rubicon": {
+            "expectRequest": {
+                "ortbRequest": {
+                    "id": "some-request-id",
+                    "site": {
+                        "page": "test.somepage.com"
+                    },
+                    "imp": [
+                        {
+                            "id": "some-imp-id",
+                            "banner": {
+                                "format": [
+                                    {
+                                        "w": 600,
+                                        "h": 500
+                                    },
+                                    {
+                                        "w": 300,
+                                        "h": 600
+                                    }
+                                ]
+                            },
+                            "ext": {
+                                "bidder": {
+                                    "accountId": 1,
+                                    "siteId": 2,
+                                    "zoneId": 3
+                                }
+                            }
+                        }
+                    ]
+                }
+            },
+            "mockResponse": {
+                "pbsSeatBids": [
+                    {
+                        "pbsBids": [
+                            {
+                                "ortbBid": {
+                                    "id": "rubi-bid",
+                                    "impid": "some-imp-id",
+                                    "price": 0.4,
+                                    "w": 200,
+                                    "h": 500,
+                                    "crid": "creative-2"
+                                },
+                                "bidType": "banner"
+                            }
+                        ],
+                        "seat": "rubicon"
+                    }
+                ]
+            }
+        }
+    },
+    "response": {
+        "bids": {
+            "id": "some-request-id",
+            "seatbid": [
+                {
+                    "seat": "appnexus",
+                    "bid": [
+                        {
+                            "id": "apn-bid",
+                            "impid": "some-imp-id",
+                            "price": 0.3,
+                            "w": 200,
+                            "h": 500,
+                            "crid": "creative-1",
+                            "ext": {
+                                "origbidcpm": 0.3,
+                                "prebid": {
+                                    "type": "banner"
+                                }
+                            }
+                        }
+                    ]
+                },
+                {
+                    "seat": "rubicon",
+                    "bid": [
+                        {
+                            "id": "rubi-bid",
+                            "impid": "some-imp-id",
+                            "price": 0.4,
+                            "w": 200,
+                            "h": 500,
+                            "crid": "creative-2",
+                            "ext": {
+                                "origbidcpm": 0.4,
+                                "prebid": {
+                                    "type": "banner"
+                                }
+                            }
+                        }
+                    ]
+                }
+            ]
+        },
+        "ext": {
+            "errors": {
+                "appnexus": [
+                    {
+                        "code": 4,
+                        "message": "bidResponse creative size warning: size WxH larger than AdUnit sizes"
+                    }
+                ],
+                "rubicon": [
+                    {
+                        "code": 4,
+                        "message": "bidResponse creative size warning: size WxH larger than AdUnit sizes"
+                    }
+                ]
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/exchange/exchangetest/bid_response_validation_warn_secure.json b/exchange/exchangetest/bid_response_validation_warn_secure.json
new file mode 100644
index 00000000..a6b208d2
--- /dev/null
+++ b/exchange/exchangetest/bid_response_validation_warn_secure.json
@@ -0,0 +1,220 @@
+{
+    "host_bid_validations": {
+        "banner_creative_max_size": "skip",
+        "secure_markup": "warn",
+        "max_creative_width": 100,
+        "max_creative_height": 100
+    },
+    "incomingRequest": {
+        "ortbRequest": {
+            "id": "some-request-id",
+            "site": {
+                "page": "test.somepage.com"
+            },
+            "imp": [
+                {
+                    "id": "some-imp-id",
+                    "banner": {
+                        "format": [
+                            {
+                                "w": 600,
+                                "h": 500
+                            },
+                            {
+                                "w": 300,
+                                "h": 600
+                            }
+                        ]
+                    },
+                    "ext": {
+                        "prebid": {
+                            "bidder": {
+                                "appnexus": {
+                                    "placementId": 1
+                                },
+                                "rubicon": {
+                                    "accountId": 1,
+                                    "siteId": 2,
+                                    "zoneId": 3
+                                }
+                            }
+                        }
+                    }
+                }
+            ]
+        }
+    },
+    "outgoingRequests": {
+        "appnexus": {
+            "expectRequest": {
+                "ortbRequest": {
+                    "id": "some-request-id",
+                    "site": {
+                        "page": "test.somepage.com"
+                    },
+                    "imp": [
+                        {
+                            "id": "some-imp-id",
+                            "banner": {
+                                "format": [
+                                    {
+                                        "w": 600,
+                                        "h": 500
+                                    },
+                                    {
+                                        "w": 300,
+                                        "h": 600
+                                    }
+                                ]
+                            },
+                            "ext": {
+                                "bidder": {
+                                    "placementId": 1
+                                }
+                            }
+                        }
+                    ]
+                }
+            },
+            "mockResponse": {
+                "pbsSeatBids": [
+                    {
+                        "pbsBids": [
+                            {
+                                "ortbBid": {
+                                    "id": "apn-bid",
+                                    "impid": "some-imp-id",
+                                    "price": 0.3,
+                                    "w": 20,
+                                    "h": 50,
+                                    "adm": "http:",
+                                    "crid": "creative-1"
+                                },
+                                "bidType": "banner"
+                            }
+                        ],
+                        "seat": "appnexus"
+                    }
+                ]
+            }
+        },
+        "rubicon": {
+            "expectRequest": {
+                "ortbRequest": {
+                    "id": "some-request-id",
+                    "site": {
+                        "page": "test.somepage.com"
+                    },
+                    "imp": [
+                        {
+                            "id": "some-imp-id",
+                            "banner": {
+                                "format": [
+                                    {
+                                        "w": 600,
+                                        "h": 500
+                                    },
+                                    {
+                                        "w": 300,
+                                        "h": 600
+                                    }
+                                ]
+                            },
+                            "ext": {
+                                "bidder": {
+                                    "accountId": 1,
+                                    "siteId": 2,
+                                    "zoneId": 3
+                                }
+                            }
+                        }
+                    ]
+                }
+            },
+            "mockResponse": {
+                "pbsSeatBids": [
+                    {
+                        "pbsBids": [
+                            {
+                                "ortbBid": {
+                                    "id": "rubi-bid",
+                                    "impid": "some-imp-id",
+                                    "price": 0.4,
+                                    "w": 20,
+                                    "h": 50,
+                                    "adm": "http:",
+                                    "crid": "creative-2"
+                                },
+                                "bidType": "banner"
+                            }
+                        ],
+                        "seat": "rubicon"
+                    }
+                ]
+            }
+        }
+    },
+    "response": {
+        "bids": {
+            "id": "some-request-id",
+            "seatbid": [
+                {
+                    "seat": "appnexus",
+                    "bid": [
+                        {
+                            "id": "apn-bid",
+                            "impid": "some-imp-id",
+                            "price": 0.3,
+                            "w": 20,
+                            "h": 50,
+                            "adm": "http:",
+                            "crid": "creative-1",
+                            "ext": {
+                                "origbidcpm": 0.3,
+                                "prebid": {
+                                    "type": "banner"
+                                }
+                            }
+                        }
+                    ]
+                },
+                {
+                    "seat": "rubicon",
+                    "bid": [
+                        {
+                            "id": "rubi-bid",
+                            "impid": "some-imp-id",
+                            "price": 0.4,
+                            "w": 20,
+                            "h": 50,
+                            "adm": "http:",
+                            "crid": "creative-2",
+                            "ext": {
+                                "origbidcpm": 0.4,
+                                "prebid": {
+                                    "type": "banner"
+                                }
+                            }
+                        }
+                    ]
+                }
+            ]
+        },
+        "ext": {
+            "errors": {
+                "appnexus": [
+                    {
+                        "code": 4,
+                        "message": "bidResponse warning: insecure creative in secure context"
+                    }
+                ],
+                "rubicon": [
+                    {
+                        "code": 4,
+                        "message": "bidResponse warning: insecure creative in secure context"
+                    }
+                ]
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/hooks/hookexecution/mocks_test.go b/hooks/hookexecution/mocks_test.go
index ab206fd9..e8670ff8 100644
--- a/hooks/hookexecution/mocks_test.go
+++ b/hooks/hookexecution/mocks_test.go
@@ -12,7 +12,7 @@ import (
 type mockUpdateHeaderEntrypointHook struct{}
 
 func (e mockUpdateHeaderEntrypointHook) HandleEntrypointHook(_ context.Context, _ hookstage.ModuleInvocationContext, _ hookstage.EntrypointPayload) (hookstage.HookResult[hookstage.EntrypointPayload], error) {
-	c := &hookstage.ChangeSet[hookstage.EntrypointPayload]{}
+	c := hookstage.ChangeSet[hookstage.EntrypointPayload]{}
 	c.AddMutation(func(payload hookstage.EntrypointPayload) (hookstage.EntrypointPayload, error) {
 		payload.Request.Header.Add("foo", "bar")
 		return payload, nil
@@ -24,7 +24,7 @@ func (e mockUpdateHeaderEntrypointHook) HandleEntrypointHook(_ context.Context,
 type mockUpdateQueryEntrypointHook struct{}
 
 func (e mockUpdateQueryEntrypointHook) HandleEntrypointHook(_ context.Context, _ hookstage.ModuleInvocationContext, _ hookstage.EntrypointPayload) (hookstage.HookResult[hookstage.EntrypointPayload], error) {
-	c := &hookstage.ChangeSet[hookstage.EntrypointPayload]{}
+	c := hookstage.ChangeSet[hookstage.EntrypointPayload]{}
 	c.AddMutation(func(payload hookstage.EntrypointPayload) (hookstage.EntrypointPayload, error) {
 		params := payload.Request.URL.Query()
 		params.Add("foo", "baz")
@@ -38,7 +38,7 @@ func (e mockUpdateQueryEntrypointHook) HandleEntrypointHook(_ context.Context, _
 type mockUpdateBodyHook struct{}
 
 func (e mockUpdateBodyHook) HandleEntrypointHook(_ context.Context, _ hookstage.ModuleInvocationContext, _ hookstage.EntrypointPayload) (hookstage.HookResult[hookstage.EntrypointPayload], error) {
-	c := &hookstage.ChangeSet[hookstage.EntrypointPayload]{}
+	c := hookstage.ChangeSet[hookstage.EntrypointPayload]{}
 	c.AddMutation(
 		func(payload hookstage.EntrypointPayload) (hookstage.EntrypointPayload, error) {
 			payload.Body = []byte(`{"name": "John", "last_name": "Doe", "foo": "bar"}`)
@@ -55,7 +55,7 @@ func (e mockUpdateBodyHook) HandleEntrypointHook(_ context.Context, _ hookstage.
 }
 
 func (e mockUpdateBodyHook) HandleRawAuctionHook(_ context.Context, _ hookstage.ModuleInvocationContext, _ hookstage.RawAuctionRequestPayload) (hookstage.HookResult[hookstage.RawAuctionRequestPayload], error) {
-	c := &hookstage.ChangeSet[hookstage.RawAuctionRequestPayload]{}
+	c := hookstage.ChangeSet[hookstage.RawAuctionRequestPayload]{}
 	c.AddMutation(
 		func(payload hookstage.RawAuctionRequestPayload) (hookstage.RawAuctionRequestPayload, error) {
 			payload = []byte(`{"name": "John", "last_name": "Doe", "foo": "bar"}`)
@@ -105,7 +105,7 @@ type mockTimeoutHook struct{}
 
 func (e mockTimeoutHook) HandleEntrypointHook(_ context.Context, _ hookstage.ModuleInvocationContext, _ hookstage.EntrypointPayload) (hookstage.HookResult[hookstage.EntrypointPayload], error) {
 	time.Sleep(20 * time.Millisecond)
-	c := &hookstage.ChangeSet[hookstage.EntrypointPayload]{}
+	c := hookstage.ChangeSet[hookstage.EntrypointPayload]{}
 	c.AddMutation(func(payload hookstage.EntrypointPayload) (hookstage.EntrypointPayload, error) {
 		params := payload.Request.URL.Query()
 		params.Add("bar", "foo")
@@ -118,7 +118,7 @@ func (e mockTimeoutHook) HandleEntrypointHook(_ context.Context, _ hookstage.Mod
 
 func (e mockTimeoutHook) HandleRawAuctionHook(_ context.Context, _ hookstage.ModuleInvocationContext, _ hookstage.RawAuctionRequestPayload) (hookstage.HookResult[hookstage.RawAuctionRequestPayload], error) {
 	time.Sleep(20 * time.Millisecond)
-	c := &hookstage.ChangeSet[hookstage.RawAuctionRequestPayload]{}
+	c := hookstage.ChangeSet[hookstage.RawAuctionRequestPayload]{}
 	c.AddMutation(func(payload hookstage.RawAuctionRequestPayload) (hookstage.RawAuctionRequestPayload, error) {
 		payload = []byte(`{"last_name": "Doe", "foo": "bar", "address": "A st."}`)
 		return payload, nil
@@ -129,7 +129,7 @@ func (e mockTimeoutHook) HandleRawAuctionHook(_ context.Context, _ hookstage.Mod
 
 func (e mockTimeoutHook) HandleProcessedAuctionHook(_ context.Context, _ hookstage.ModuleInvocationContext, _ hookstage.ProcessedAuctionRequestPayload) (hookstage.HookResult[hookstage.ProcessedAuctionRequestPayload], error) {
 	time.Sleep(20 * time.Millisecond)
-	c := &hookstage.ChangeSet[hookstage.ProcessedAuctionRequestPayload]{}
+	c := hookstage.ChangeSet[hookstage.ProcessedAuctionRequestPayload]{}
 	c.AddMutation(func(payload hookstage.ProcessedAuctionRequestPayload) (hookstage.ProcessedAuctionRequestPayload, error) {
 		payload.BidRequest.User.CustomData = "some-custom-data"
 		return payload, nil
@@ -140,7 +140,7 @@ func (e mockTimeoutHook) HandleProcessedAuctionHook(_ context.Context, _ hooksta
 
 func (e mockTimeoutHook) HandleBidderRequestHook(_ context.Context, _ hookstage.ModuleInvocationContext, _ hookstage.BidderRequestPayload) (hookstage.HookResult[hookstage.BidderRequestPayload], error) {
 	time.Sleep(20 * time.Millisecond)
-	c := &hookstage.ChangeSet[hookstage.BidderRequestPayload]{}
+	c := hookstage.ChangeSet[hookstage.BidderRequestPayload]{}
 	c.AddMutation(func(payload hookstage.BidderRequestPayload) (hookstage.BidderRequestPayload, error) {
 		payload.BidRequest.User.CustomData = "some-custom-data"
 		return payload, nil
@@ -151,7 +151,7 @@ func (e mockTimeoutHook) HandleBidderRequestHook(_ context.Context, _ hookstage.
 
 func (e mockTimeoutHook) HandleRawBidderResponseHook(_ context.Context, _ hookstage.ModuleInvocationContext, _ hookstage.RawBidderResponsePayload) (hookstage.HookResult[hookstage.RawBidderResponsePayload], error) {
 	time.Sleep(20 * time.Millisecond)
-	c := &hookstage.ChangeSet[hookstage.RawBidderResponsePayload]{}
+	c := hookstage.ChangeSet[hookstage.RawBidderResponsePayload]{}
 	c.AddMutation(func(payload hookstage.RawBidderResponsePayload) (hookstage.RawBidderResponsePayload, error) {
 		payload.Bids[0].BidMeta = &openrtb_ext.ExtBidPrebidMeta{AdapterCode: "new-code"}
 		return payload, nil
@@ -162,7 +162,7 @@ func (e mockTimeoutHook) HandleRawBidderResponseHook(_ context.Context, _ hookst
 
 func (e mockTimeoutHook) HandleAllProcessedBidResponsesHook(_ context.Context, _ hookstage.ModuleInvocationContext, _ hookstage.AllProcessedBidResponsesPayload) (hookstage.HookResult[hookstage.AllProcessedBidResponsesPayload], error) {
 	time.Sleep(20 * time.Millisecond)
-	c := &hookstage.ChangeSet[hookstage.AllProcessedBidResponsesPayload]{}
+	c := hookstage.ChangeSet[hookstage.AllProcessedBidResponsesPayload]{}
 	c.AddMutation(func(payload hookstage.AllProcessedBidResponsesPayload) (hookstage.AllProcessedBidResponsesPayload, error) {
 		payload.Responses["some-bidder"].Bids[0].BidMeta = &openrtb_ext.ExtBidPrebidMeta{AdapterCode: "new-code"}
 		return payload, nil
@@ -173,7 +173,7 @@ func (e mockTimeoutHook) HandleAllProcessedBidResponsesHook(_ context.Context, _
 
 func (e mockTimeoutHook) HandleAuctionResponseHook(_ context.Context, _ hookstage.ModuleInvocationContext, _ hookstage.AuctionResponsePayload) (hookstage.HookResult[hookstage.AuctionResponsePayload], error) {
 	time.Sleep(2 * time.Millisecond)
-	c := &hookstage.ChangeSet[hookstage.AuctionResponsePayload]{}
+	c := hookstage.ChangeSet[hookstage.AuctionResponsePayload]{}
 	c.AddMutation(func(payload hookstage.AuctionResponsePayload) (hookstage.AuctionResponsePayload, error) {
 		payload.BidResponse.Ext = []byte("another-byte")
 		return payload, nil
@@ -264,7 +264,7 @@ func (h mockErrorHook) HandleAuctionResponseHook(_ context.Context, miCtx hookst
 type mockFailedMutationHook struct{}
 
 func (h mockFailedMutationHook) HandleEntrypointHook(_ context.Context, _ hookstage.ModuleInvocationContext, _ hookstage.EntrypointPayload) (hookstage.HookResult[hookstage.EntrypointPayload], error) {
-	changeSet := &hookstage.ChangeSet[hookstage.EntrypointPayload]{}
+	changeSet := hookstage.ChangeSet[hookstage.EntrypointPayload]{}
 	changeSet.AddMutation(func(payload hookstage.EntrypointPayload) (hookstage.EntrypointPayload, error) {
 		return payload, errors.New("key not found")
 	}, hookstage.MutationUpdate, "header", "foo")
@@ -273,7 +273,7 @@ func (h mockFailedMutationHook) HandleEntrypointHook(_ context.Context, _ hookst
 }
 
 func (h mockFailedMutationHook) HandleRawAuctionHook(_ context.Context, miCtx hookstage.ModuleInvocationContext, _ hookstage.RawAuctionRequestPayload) (hookstage.HookResult[hookstage.RawAuctionRequestPayload], error) {
-	changeSet := &hookstage.ChangeSet[hookstage.RawAuctionRequestPayload]{}
+	changeSet := hookstage.ChangeSet[hookstage.RawAuctionRequestPayload]{}
 	changeSet.AddMutation(func(payload hookstage.RawAuctionRequestPayload) (hookstage.RawAuctionRequestPayload, error) {
 		return payload, errors.New("key not found")
 	}, hookstage.MutationUpdate, "header", "foo")
@@ -282,7 +282,7 @@ func (h mockFailedMutationHook) HandleRawAuctionHook(_ context.Context, miCtx ho
 }
 
 func (h mockFailedMutationHook) HandleBidderRequestHook(_ context.Context, _ hookstage.ModuleInvocationContext, _ hookstage.BidderRequestPayload) (hookstage.HookResult[hookstage.BidderRequestPayload], error) {
-	changeSet := &hookstage.ChangeSet[hookstage.BidderRequestPayload]{}
+	changeSet := hookstage.ChangeSet[hookstage.BidderRequestPayload]{}
 	changeSet.AddMutation(func(payload hookstage.BidderRequestPayload) (hookstage.BidderRequestPayload, error) {
 		return payload, errors.New("key not found")
 	}, hookstage.MutationUpdate, "header", "foo")
@@ -291,7 +291,7 @@ func (h mockFailedMutationHook) HandleBidderRequestHook(_ context.Context, _ hoo
 }
 
 func (h mockFailedMutationHook) HandleAllProcessedBidResponsesHook(_ context.Context, _ hookstage.ModuleInvocationContext, _ hookstage.AllProcessedBidResponsesPayload) (hookstage.HookResult[hookstage.AllProcessedBidResponsesPayload], error) {
-	changeSet := &hookstage.ChangeSet[hookstage.AllProcessedBidResponsesPayload]{}
+	changeSet := hookstage.ChangeSet[hookstage.AllProcessedBidResponsesPayload]{}
 	changeSet.AddMutation(func(payload hookstage.AllProcessedBidResponsesPayload) (hookstage.AllProcessedBidResponsesPayload, error) {
 		return payload, errors.New("key not found")
 	}, hookstage.MutationUpdate, "some-bidder", "bids[0]", "deal_priority")
@@ -302,7 +302,7 @@ func (h mockFailedMutationHook) HandleAllProcessedBidResponsesHook(_ context.Con
 type mockUpdateBidRequestHook struct{}
 
 func (e mockUpdateBidRequestHook) HandleProcessedAuctionHook(_ context.Context, _ hookstage.ModuleInvocationContext, _ hookstage.ProcessedAuctionRequestPayload) (hookstage.HookResult[hookstage.ProcessedAuctionRequestPayload], error) {
-	c := &hookstage.ChangeSet[hookstage.ProcessedAuctionRequestPayload]{}
+	c := hookstage.ChangeSet[hookstage.ProcessedAuctionRequestPayload]{}
 	c.AddMutation(
 		func(payload hookstage.ProcessedAuctionRequestPayload) (hookstage.ProcessedAuctionRequestPayload, error) {
 			payload.BidRequest.User.Yob = 2000
@@ -319,7 +319,7 @@ func (e mockUpdateBidRequestHook) HandleProcessedAuctionHook(_ context.Context,
 }
 
 func (e mockUpdateBidRequestHook) HandleBidderRequestHook(_ context.Context, _ hookstage.ModuleInvocationContext, _ hookstage.BidderRequestPayload) (hookstage.HookResult[hookstage.BidderRequestPayload], error) {
-	c := &hookstage.ChangeSet[hookstage.BidderRequestPayload]{}
+	c := hookstage.ChangeSet[hookstage.BidderRequestPayload]{}
 	c.AddMutation(
 		func(payload hookstage.BidderRequestPayload) (hookstage.BidderRequestPayload, error) {
 			payload.BidRequest.User.Yob = 2000
@@ -338,7 +338,7 @@ func (e mockUpdateBidRequestHook) HandleBidderRequestHook(_ context.Context, _ h
 type mockUpdateBidderResponseHook struct{}
 
 func (e mockUpdateBidderResponseHook) HandleRawBidderResponseHook(_ context.Context, _ hookstage.ModuleInvocationContext, _ hookstage.RawBidderResponsePayload) (hookstage.HookResult[hookstage.RawBidderResponsePayload], error) {
-	c := &hookstage.ChangeSet[hookstage.RawBidderResponsePayload]{}
+	c := hookstage.ChangeSet[hookstage.RawBidderResponsePayload]{}
 	c.AddMutation(
 		func(payload hookstage.RawBidderResponsePayload) (hookstage.RawBidderResponsePayload, error) {
 			payload.Bids[0].DealPriority = 10
@@ -352,7 +352,7 @@ func (e mockUpdateBidderResponseHook) HandleRawBidderResponseHook(_ context.Cont
 type mockUpdateBiddersResponsesHook struct{}
 
 func (e mockUpdateBiddersResponsesHook) HandleAllProcessedBidResponsesHook(_ context.Context, _ hookstage.ModuleInvocationContext, _ hookstage.AllProcessedBidResponsesPayload) (hookstage.HookResult[hookstage.AllProcessedBidResponsesPayload], error) {
-	c := &hookstage.ChangeSet[hookstage.AllProcessedBidResponsesPayload]{}
+	c := hookstage.ChangeSet[hookstage.AllProcessedBidResponsesPayload]{}
 	c.AddMutation(
 		func(payload hookstage.AllProcessedBidResponsesPayload) (hookstage.AllProcessedBidResponsesPayload, error) {
 			payload.Responses["some-bidder"].Bids[0].DealPriority = 10
@@ -366,7 +366,7 @@ func (e mockUpdateBiddersResponsesHook) HandleAllProcessedBidResponsesHook(_ con
 type mockUpdateBidResponseHook struct{}
 
 func (e mockUpdateBidResponseHook) HandleAuctionResponseHook(_ context.Context, _ hookstage.ModuleInvocationContext, _ hookstage.AuctionResponsePayload) (hookstage.HookResult[hookstage.AuctionResponsePayload], error) {
-	c := &hookstage.ChangeSet[hookstage.AuctionResponsePayload]{}
+	c := hookstage.ChangeSet[hookstage.AuctionResponsePayload]{}
 	c.AddMutation(
 		func(payload hookstage.AuctionResponsePayload) (hookstage.AuctionResponsePayload, error) {
 			payload.BidResponse.CustomData = "new-custom-data"
diff --git a/metrics/go_metrics_test.go b/metrics/go_metrics_test.go
index d3bd2d66..7be83638 100644
--- a/metrics/go_metrics_test.go
+++ b/metrics/go_metrics_test.go
@@ -130,6 +130,12 @@ func ensureContainsAdapterMetrics(t *testing.T, registry metrics.Registry, name
 	ensureContains(t, registry, name+".connections_created", adapterMetrics.ConnCreated)
 	ensureContains(t, registry, name+".connections_reused", adapterMetrics.ConnReused)
 	ensureContains(t, registry, name+".connection_wait_time", adapterMetrics.ConnWaitTime)
+
+	ensureContains(t, registry, name+".response.validation.size.err", adapterMetrics.BidValidationCreativeSizeErrorMeter)
+	ensureContains(t, registry, name+".response.validation.size.warn", adapterMetrics.BidValidationCreativeSizeWarnMeter)
+	ensureContains(t, registry, name+".response.validation.secure.err", adapterMetrics.BidValidationSecureMarkupErrorMeter)
+	ensureContains(t, registry, name+".response.validation.secure.warn", adapterMetrics.BidValidationSecureMarkupWarnMeter)
+
 }
 
 func ensureContainsModuleMetrics(t *testing.T, registry metrics.Registry, name string, moduleMetrics *ModuleMetrics) {
@@ -272,6 +278,90 @@ func TestRecordDebugRequest(t *testing.T) {
 	}
 }
 
+func TestRecordBidValidationCreativeSize(t *testing.T) {
+	testCases := []struct {
+		description          string
+		givenDisabledMetrics config.DisabledMetrics
+		givenPubID           string
+		expectedAccountCount int64
+		expectedAdapterCount int64
+	}{
+		{
+			description: "Account Metric isn't disabled, so both metrics should be incremented",
+			givenDisabledMetrics: config.DisabledMetrics{
+				AccountAdapterDetails: false,
+			},
+			givenPubID:           "acct-id",
+			expectedAdapterCount: 1,
+			expectedAccountCount: 1,
+		},
+		{
+			description: "Account Metric is disabled, so only the adapter metric should increment",
+			givenDisabledMetrics: config.DisabledMetrics{
+				AccountAdapterDetails: true,
+			},
+			givenPubID:           "acct-id",
+			expectedAdapterCount: 1,
+			expectedAccountCount: 0,
+		},
+	}
+	for _, test := range testCases {
+		registry := metrics.NewRegistry()
+		m := NewMetrics(registry, []openrtb_ext.BidderName{openrtb_ext.BidderAppnexus}, test.givenDisabledMetrics, nil, nil)
+
+		m.RecordBidValidationCreativeSizeError(openrtb_ext.BidderAppnexus, test.givenPubID)
+		m.RecordBidValidationCreativeSizeWarn(openrtb_ext.BidderAppnexus, test.givenPubID)
+		am := m.getAccountMetrics(test.givenPubID)
+
+		assert.Equal(t, test.expectedAdapterCount, m.AdapterMetrics[openrtb_ext.BidderAppnexus].BidValidationCreativeSizeErrorMeter.Count())
+		assert.Equal(t, test.expectedAdapterCount, m.AdapterMetrics[openrtb_ext.BidderAppnexus].BidValidationCreativeSizeWarnMeter.Count())
+		assert.Equal(t, test.expectedAccountCount, am.bidValidationCreativeSizeMeter.Count())
+		assert.Equal(t, test.expectedAccountCount, am.bidValidationCreativeSizeWarnMeter.Count())
+	}
+}
+
+func TestRecordBidValidationSecureMarkup(t *testing.T) {
+	testCases := []struct {
+		description          string
+		givenDisabledMetrics config.DisabledMetrics
+		givenPubID           string
+		expectedAccountCount int64
+		expectedAdapterCount int64
+	}{
+		{
+			description: "Account Metric isn't disabled, so both metrics should be incremented",
+			givenDisabledMetrics: config.DisabledMetrics{
+				AccountAdapterDetails: false,
+			},
+			givenPubID:           "acct-id",
+			expectedAdapterCount: 1,
+			expectedAccountCount: 1,
+		},
+		{
+			description: "Account Metric is disabled, so only the adapter metric should increment",
+			givenDisabledMetrics: config.DisabledMetrics{
+				AccountAdapterDetails: true,
+			},
+			givenPubID:           "acct-id",
+			expectedAdapterCount: 1,
+			expectedAccountCount: 0,
+		},
+	}
+	for _, test := range testCases {
+		registry := metrics.NewRegistry()
+		m := NewMetrics(registry, []openrtb_ext.BidderName{openrtb_ext.BidderAppnexus}, test.givenDisabledMetrics, nil, nil)
+
+		m.RecordBidValidationSecureMarkupError(openrtb_ext.BidderAppnexus, test.givenPubID)
+		m.RecordBidValidationSecureMarkupWarn(openrtb_ext.BidderAppnexus, test.givenPubID)
+		am := m.getAccountMetrics(test.givenPubID)
+
+		assert.Equal(t, test.expectedAdapterCount, m.AdapterMetrics[openrtb_ext.BidderAppnexus].BidValidationSecureMarkupErrorMeter.Count())
+		assert.Equal(t, test.expectedAdapterCount, m.AdapterMetrics[openrtb_ext.BidderAppnexus].BidValidationSecureMarkupWarnMeter.Count())
+		assert.Equal(t, test.expectedAccountCount, am.bidValidationSecureMarkupMeter.Count())
+		assert.Equal(t, test.expectedAccountCount, am.bidValidationSecureMarkupWarnMeter.Count())
+	}
+}
+
 func TestRecordDNSTime(t *testing.T) {
 	testCases := []struct {
 		description         string
diff --git a/metrics/prometheus/prometheus_test.go b/metrics/prometheus/prometheus_test.go
index ce1fa085..17b9be83 100644
--- a/metrics/prometheus/prometheus_test.go
+++ b/metrics/prometheus/prometheus_test.go
@@ -65,7 +65,7 @@ func TestMetricCountGatekeeping(t *testing.T) {
 	// Verify Per-Adapter Cardinality
 	// - This assertion provides a warning for newly added adapter metrics. Threre are 40+ adapters which makes the
 	//   cost of new per-adapter metrics rather expensive. Thought should be given when adding new per-adapter metrics.
-	assert.True(t, perAdapterCardinalityCount <= 27, "Per-Adapter Cardinality count equals %d \n", perAdapterCardinalityCount)
+	assert.True(t, perAdapterCardinalityCount <= 29, "Per-Adapter Cardinality count equals %d \n", perAdapterCardinalityCount)
 }
 
 func TestConnectionMetrics(t *testing.T) {
@@ -206,6 +206,78 @@ func TestDebugRequestMetric(t *testing.T) {
 	}
 }
 
+func TestBidValidationCreativeSizeMetric(t *testing.T) {
+	testCases := []struct {
+		description                        string
+		givenDebugEnabledFlag              bool
+		givenAccountAdapterMetricsDisabled bool
+		expectedAdapterCount               float64
+		expectedAccountCount               float64
+	}{
+		{
+			description:                        "Account Metric isn't disabled, so both metrics should be incremented",
+			givenAccountAdapterMetricsDisabled: false,
+			expectedAdapterCount:               1,
+			expectedAccountCount:               1,
+		},
+		{
+			description:                        "Account Metric is disabled, so only adapter metric should be incremented",
+			givenAccountAdapterMetricsDisabled: true,
+			expectedAdapterCount:               1,
+			expectedAccountCount:               0,
+		},
+	}
+
+	for _, test := range testCases {
+		m := createMetricsForTesting()
+		m.metricsDisabled.AccountAdapterDetails = test.givenAccountAdapterMetricsDisabled
+		m.RecordBidValidationCreativeSizeError(adapterLabel, "acct-id")
+		m.RecordBidValidationCreativeSizeWarn(adapterLabel, "acct-id")
+
+		assertCounterVecValue(t, "", "account bid validation", m.accountBidResponseValidationSizeError, test.expectedAccountCount, prometheus.Labels{accountLabel: "acct-id", successLabel: successLabel})
+		assertCounterVecValue(t, "", "adapter bid validation", m.adapterBidResponseValidationSizeError, test.expectedAdapterCount, prometheus.Labels{adapterLabel: adapterLabel, successLabel: successLabel})
+
+		assertCounterVecValue(t, "", "account bid validation", m.accountBidResponseValidationSizeWarn, test.expectedAccountCount, prometheus.Labels{accountLabel: "acct-id", successLabel: successLabel})
+		assertCounterVecValue(t, "", "adapter bid validation", m.adapterBidResponseValidationSizeWarn, test.expectedAdapterCount, prometheus.Labels{adapterLabel: adapterLabel, successLabel: successLabel})
+	}
+}
+
+func TestBidValidationSecureMarkupMetric(t *testing.T) {
+	testCases := []struct {
+		description                        string
+		givenDebugEnabledFlag              bool
+		givenAccountAdapterMetricsDisabled bool
+		expectedAdapterCount               float64
+		expectedAccountCount               float64
+	}{
+		{
+			description:                        "Account Metric isn't disabled, so both metrics should be incremented",
+			givenAccountAdapterMetricsDisabled: false,
+			expectedAdapterCount:               1,
+			expectedAccountCount:               1,
+		},
+		{
+			description:                        "Account Metric is disabled, so only adapter metric should be incremented",
+			givenAccountAdapterMetricsDisabled: true,
+			expectedAdapterCount:               1,
+			expectedAccountCount:               0,
+		},
+	}
+
+	for _, test := range testCases {
+		m := createMetricsForTesting()
+		m.metricsDisabled.AccountAdapterDetails = test.givenAccountAdapterMetricsDisabled
+		m.RecordBidValidationSecureMarkupError(adapterLabel, "acct-id")
+		m.RecordBidValidationSecureMarkupWarn(adapterLabel, "acct-id")
+
+		assertCounterVecValue(t, "", "Account Secure Markup Error", m.accountBidResponseSecureMarkupError, test.expectedAccountCount, prometheus.Labels{accountLabel: "acct-id", successLabel: successLabel})
+		assertCounterVecValue(t, "", "Adapter Secure Markup Error", m.adapterBidResponseSecureMarkupError, test.expectedAdapterCount, prometheus.Labels{adapterLabel: adapterLabel, successLabel: successLabel})
+
+		assertCounterVecValue(t, "", "Account Secure Markup Warn", m.accountBidResponseSecureMarkupWarn, test.expectedAccountCount, prometheus.Labels{accountLabel: "acct-id", successLabel: successLabel})
+		assertCounterVecValue(t, "", "Adapter Secure Markup Warn", m.adapterBidResponseSecureMarkupWarn, test.expectedAdapterCount, prometheus.Labels{adapterLabel: adapterLabel, successLabel: successLabel})
+	}
+}
+
 func TestRequestMetricWithoutCookie(t *testing.T) {
 	requestType := metrics.ReqTypeORTB2Web
 	performTest := func(m *Metrics, cookieFlag metrics.CookieFlag) {
diff --git a/modules/modules_test.go b/modules/modules_test.go
index d7c150a6..1c70ce4b 100644
--- a/modules/modules_test.go
+++ b/modules/modules_test.go
@@ -12,76 +12,80 @@ import (
 	"github.com/prebid/prebid-server/config"
 	"github.com/prebid/prebid-server/hooks"
 	"github.com/prebid/prebid-server/hooks/hookstage"
+	"github.com/prebid/prebid-server/modules/moduledeps"
 	"github.com/stretchr/testify/assert"
 )
 
 func TestModuleBuilderBuild(t *testing.T) {
 	vendor := "acme"
 	moduleName := "foobar"
+	defaultModulesConfig := map[string]map[string]interface{}{vendor: {moduleName: map[string]interface{}{"enabled": true}}}
+	defaultHookRepository, err := hooks.NewHookRepository(map[string]interface{}{vendor + "." + moduleName: module{}})
+	if err != nil {
+		t.Fatalf("Failed to init default hook repository: %s", err)
+	}
+	emptyHookRepository, err := hooks.NewHookRepository(map[string]interface{}{})
+	if err != nil {
+		t.Fatalf("Failed to init empty hook repository: %s", err)
+	}
 
 	testCases := map[string]struct {
-		isHookFound          bool
-		expectedModStageColl map[string][]string
-		expectedHook         interface{}
-		givenModule          interface{}
-		givenConfig          config.Modules
-		expectedErr          error
-		givenHookBuilderErr  error
-		givenGetHookFn       func(repo hooks.HookRepository, module string) (interface{}, bool)
+		givenModule           interface{}
+		givenConfig           config.Modules
+		givenHookBuilderErr   error
+		expectedHookRepo      hooks.HookRepository
+		expectedModulesStages map[string][]string
+		expectedErr           error
 	}{
-		"Can build with entrypoint hook without config": {
-			isHookFound:          true,
-			expectedModStageColl: map[string][]string{vendor + "_" + moduleName: {hooks.StageEntrypoint.String(), hooks.StageAuctionResponse.String()}},
-			expectedHook:         module{},
-			givenModule:          module{},
-			givenGetHookFn: func(repo hooks.HookRepository, module string) (interface{}, bool) {
-				return repo.GetEntrypointHook(module)
-			},
+		"Can build module with config": {
+			givenModule:           module{},
+			givenConfig:           defaultModulesConfig,
+			expectedModulesStages: map[string][]string{vendor + "_" + moduleName: {hooks.StageEntrypoint.String(), hooks.StageAuctionResponse.String()}},
+			expectedHookRepo:      defaultHookRepository,
+			expectedErr:           nil,
 		},
-		"Can build with entrypoint hook with config": {
-			isHookFound:          true,
-			expectedModStageColl: map[string][]string{vendor + "_" + moduleName: {hooks.StageEntrypoint.String(), hooks.StageAuctionResponse.String()}},
-			expectedHook:         module{},
-			givenModule:          module{},
-			givenConfig:          map[string]map[string]interface{}{vendor: {moduleName: map[string]bool{"enabled": true}}},
-			givenGetHookFn: func(repo hooks.HookRepository, module string) (interface{}, bool) {
-				return repo.GetEntrypointHook(module)
-			},
+		"Module is not added to hook repository if it's disabled": {
+			givenModule:           module{},
+			givenConfig:           map[string]map[string]interface{}{vendor: {moduleName: map[string]interface{}{"enabled": false, "attr": "val"}}},
+			expectedModulesStages: map[string][]string{},
+			expectedHookRepo:      emptyHookRepository,
+			expectedErr:           nil,
 		},
-		"Can build with auction response hook": {
-			isHookFound:          true,
-			expectedModStageColl: map[string][]string{vendor + "_" + moduleName: {hooks.StageEntrypoint.String(), hooks.StageAuctionResponse.String()}},
-			expectedHook:         module{},
-			givenModule:          module{},
-			givenConfig:          map[string]map[string]interface{}{"vendor": {"module": map[string]bool{"enabled": true}}},
-			givenGetHookFn: func(repo hooks.HookRepository, module string) (interface{}, bool) {
-				return repo.GetAuctionResponseHook(module)
-			},
+		"Module considered disabled if status property not defined in module config": {
+			givenModule:           module{},
+			givenConfig:           map[string]map[string]interface{}{vendor: {moduleName: map[string]interface{}{"foo": "bar"}}},
+			expectedHookRepo:      emptyHookRepository,
+			expectedModulesStages: map[string][]string{},
+			expectedErr:           nil,
 		},
-		"Fails to find not registered hook": {
-			isHookFound:          false,
-			expectedModStageColl: map[string][]string{vendor + "_" + moduleName: {hooks.StageEntrypoint.String(), hooks.StageAuctionResponse.String()}},
-			expectedHook:         nil,
-			givenModule:          module{},
-			givenConfig:          map[string]map[string]interface{}{vendor: {"module": map[string]bool{"enabled": true}}},
-			givenGetHookFn: func(repo hooks.HookRepository, module string) (interface{}, bool) {
-				return repo.GetAllProcessedBidResponsesHook(module) // ask for hook not implemented in module
-			},
+		"Module considered disabled if its config not provided and as a result skipped from execution": {
+			givenModule:           module{},
+			givenConfig:           nil,
+			expectedHookRepo:      emptyHookRepository,
+			expectedModulesStages: map[string][]string{},
+			expectedErr:           nil,
 		},
 		"Fails if module does not implement any hook interface": {
-			expectedHook: struct{}{},
-			expectedErr:  fmt.Errorf(`hook "%s.%s" does not implement any supported hook interface`, vendor, moduleName),
+			givenModule:           struct{}{},
+			givenConfig:           defaultModulesConfig,
+			expectedHookRepo:      nil,
+			expectedModulesStages: nil,
+			expectedErr:           fmt.Errorf(`hook "%s.%s" does not implement any supported hook interface`, vendor, moduleName),
 		},
 		"Fails if module builder function returns error": {
-			givenModule:         module{},
-			givenConfig:         map[string]map[string]interface{}{vendor: {moduleName: map[string]string{"media_type": "video"}}},
-			givenHookBuilderErr: errors.New("failed to build module"),
-			expectedErr:         fmt.Errorf(`failed to init "%s.%s" module: %s`, vendor, moduleName, "failed to build module"),
+			givenModule:           module{},
+			givenConfig:           defaultModulesConfig,
+			givenHookBuilderErr:   errors.New("failed to build module"),
+			expectedHookRepo:      nil,
+			expectedModulesStages: nil,
+			expectedErr:           fmt.Errorf(`failed to init "%s.%s" module: %s`, vendor, moduleName, "failed to build module"),
 		},
 		"Fails if config marshaling returns error": {
-			givenModule: module{},
-			givenConfig: map[string]map[string]interface{}{vendor: {moduleName: math.Inf(1)}},
-			expectedErr: fmt.Errorf(`failed to marshal "%s.%s" module config: json: unsupported value: +Inf`, vendor, moduleName),
+			givenModule:           module{},
+			givenConfig:           map[string]map[string]interface{}{vendor: {moduleName: math.Inf(1)}},
+			expectedHookRepo:      nil,
+			expectedModulesStages: nil,
+			expectedErr:           fmt.Errorf(`failed to marshal "%s.%s" module config: json: unsupported value: +Inf`, vendor, moduleName),
 		},
 	}
 
@@ -90,21 +94,17 @@ func TestModuleBuilderBuild(t *testing.T) {
 			builder := &builder{
 				builders: ModuleBuilders{
 					vendor: {
-						moduleName: func(cfg json.RawMessage, client *http.Client) (interface{}, error) {
+						moduleName: func(cfg json.RawMessage, deps moduledeps.ModuleDeps) (interface{}, error) {
 							return test.givenModule, test.givenHookBuilderErr
 						},
 					},
 				},
 			}
 
-			repo, coll, err := builder.Build(test.givenConfig, http.DefaultClient)
+			repo, modulesStages, err := builder.Build(test.givenConfig, moduledeps.ModuleDeps{HTTPClient: http.DefaultClient})
 			assert.Equal(t, test.expectedErr, err)
-			if test.expectedErr == nil {
-				hook, found := test.givenGetHookFn(repo, fmt.Sprintf("%s.%s", vendor, moduleName))
-				assert.Equal(t, test.isHookFound, found)
-				assert.IsType(t, test.expectedHook, hook)
-				assert.Equal(t, test.expectedModStageColl, coll)
-			}
+			assert.Equal(t, test.expectedModulesStages, modulesStages)
+			assert.Equal(t, test.expectedHookRepo, repo)
 		})
 	}
 }
diff --git a/modules/prebid/ortb2blocking/config_test.go b/modules/prebid/ortb2blocking/config_test.go
new file mode 100644
index 00000000..18a51ed5
--- /dev/null
+++ b/modules/prebid/ortb2blocking/config_test.go
@@ -0,0 +1,339 @@
+package ortb2blocking
+
+import (
+	"testing"
+
+	"github.com/prebid/openrtb/v17/adcom1"
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
+)
+
+var fullConfig = []byte(`
+{
+  "attributes": {
+    "badv": {
+      "enforce_blocks": true,
+      "block_unknown_adomain": true,
+      "blocked_adomain": [
+        "a.com",
+        "b.com",
+        "c.com"
+      ],
+      "allowed_adomain_for_deals": [
+        "z.com",
+        "x.com"
+      ],
+      "action_overrides": {
+        "blocked_adomain": [
+          {
+            "conditions": {
+              "bidders": [
+                "bidderA",
+                "bidderB"
+              ],
+              "media_types": [
+                "video"
+              ]
+            },
+            "override": [
+              "a.com",
+              "b.com"
+            ]
+          },
+          {
+            "conditions": {
+              "bidders": [
+                "bidderB"
+              ]
+            },
+            "override": [
+              "a.com",
+              "b.com",
+              "c.com",
+              "d.com",
+              "e.com"
+            ]
+          }
+        ],
+        "block_unknown_adomain": [
+          {
+            "conditions": {
+              "bidders": [
+                "bidderA"
+              ],
+              "media_types": [
+                "video"
+              ]
+            },
+            "override": true
+          }
+        ],
+        "allowed_adomain_for_deals": [
+          {
+            "conditions": {
+              "deal_ids": [
+                "12345678"
+              ]
+            },
+            "override": [
+              "a.com"
+            ]
+          }
+        ]
+      }
+    },
+    "bcat": {
+      "enforce_blocks": false,
+      "block_unknown_adv_cat": false,
+      "category_taxonomy": 6,
+      "blocked_adv_cat": [
+        "IAB-1",
+        "IAB-2"
+      ],
+      "allowed_adv_cat_for_deals": [
+        "IAB-1"
+      ],
+      "action_overrides": {
+        "blocked_adv_cat": [
+          {
+            "conditions": {
+              "media_types": [
+                "video"
+              ]
+            },
+            "override": [
+              "IAB-1",
+              "IAB-2",
+              "IAB-3",
+              "IAB-4"
+            ]
+          }
+        ],
+        "enforce_blocks": [
+          {
+            "conditions": {
+              "bidders": [
+                "bidderA"
+              ]
+            },
+            "override": true
+          }
+        ],
+        "block_unknown_adv_cat": [
+          {
+            "conditions": {
+              "media_types": [
+                "video"
+              ]
+            },
+            "override": true
+          }
+        ],
+        "allowed_adv_cat_for_deals": [
+          {
+            "conditions": {
+              "deal_ids": [
+                "1111111"
+              ]
+            },
+            "override": [
+              "IAB-1"
+            ]
+          }
+        ]
+      }
+    },
+    "bapp": {
+      "enforce_blocks": false,
+      "blocked_app": [
+        "app1",
+        "app2"
+      ],
+      "action_overrides": {
+        "blocked_app": [
+          {
+            "conditions": {
+              "bidders": [
+                "bidderA"
+              ]
+            },
+            "override": [
+              "app3"
+            ]
+          }
+        ]
+      }
+    },
+    "btype": {
+      "blocked_banner_type": [
+        3,
+        4
+      ],
+      "action_overrides": {
+        "blocked_banner_type": [
+          {
+            "conditions": {
+              "bidders": [
+                "bidderA"
+              ]
+            },
+            "override": [
+              3,
+              4,
+              5
+            ]
+          }
+        ]
+      }
+    },
+    "battr": {
+      "enforce_blocks": false,
+      "blocked_banner_attr": [
+        1,
+        8,
+        9,
+        10
+      ],
+      "action_overrides": {
+        "enforce_blocks": [
+          {
+            "conditions": {
+              "bidders": [
+                "bidderA"
+              ]
+            },
+            "override": true
+          }
+        ]
+      }
+    }
+  }
+}
+`)
+
+func TestNewConfig(t *testing.T) {
+	c, err := newConfig(fullConfig)
+	require.NoError(t, err)
+
+	// badv
+	assert.True(t, c.Attributes.Badv.EnforceBlocks, "attributes.badv.enforce_blocks")
+	assert.True(t, c.Attributes.Badv.BlockUnknownAdomain, "attributes.badv.block_unknown_adomain")
+	assert.Equal(t, []string{"a.com", "b.com", "c.com"}, c.Attributes.Badv.BlockedAdomain, "attributes.badv.blocked_adomain")
+	assert.Equal(t, []string{"z.com", "x.com"}, c.Attributes.Badv.AllowedAdomainForDeals, "attributes.badv.allowed_adomain_for_deals")
+
+	assert.Empty(t, c.Attributes.Badv.ActionOverrides.EnforceBlocks, "attributes.badv.action_overrides[0].enforce_blocks")
+
+	assert.Equal(t, []string{"bidderA", "bidderB"}, c.Attributes.Badv.ActionOverrides.BlockedAdomain[0].Conditions.Bidders, "attributes.badv.action_overrides[0].blocked_adomain[0].conditions.bidders")
+	assert.Equal(t, []string{"video"}, c.Attributes.Badv.ActionOverrides.BlockedAdomain[0].Conditions.MediaTypes, "attributes.badv.action_overrides[0].blocked_adomain[0].conditions.media_types")
+	assert.Empty(t, c.Attributes.Badv.ActionOverrides.BlockedAdomain[0].Conditions.DealIds, "attributes.badv.action_overrides[0].blocked_adomain[0].conditions.deal_ids")
+	assert.False(t, c.Attributes.Badv.ActionOverrides.BlockedAdomain[0].Override.IsActive, "attributes.badv.action_overrides[0].blocked_adomain[0].override")
+	assert.Empty(t, c.Attributes.Badv.ActionOverrides.BlockedAdomain[0].Override.Ids, "attributes.badv.action_overrides[0].blocked_adomain[0].override")
+	assert.Equal(t, []string{"a.com", "b.com"}, c.Attributes.Badv.ActionOverrides.BlockedAdomain[0].Override.Names, "attributes.badv.action_overrides[0].blocked_adomain[0].override")
+
+	assert.Equal(t, []string{"bidderB"}, c.Attributes.Badv.ActionOverrides.BlockedAdomain[1].Conditions.Bidders, "attributes.badv.action_overrides[0].blocked_adomain[1].conditions.bidders")
+	assert.Empty(t, c.Attributes.Badv.ActionOverrides.BlockedAdomain[1].Conditions.MediaTypes, "attributes.badv.action_overrides[0].blocked_adomain[1].conditions.media_types")
+	assert.Empty(t, c.Attributes.Badv.ActionOverrides.BlockedAdomain[1].Conditions.DealIds, "attributes.badv.action_overrides[0].blocked_adomain[1].conditions.deal_ids")
+	assert.False(t, c.Attributes.Badv.ActionOverrides.BlockedAdomain[1].Override.IsActive, "attributes.badv.action_overrides[0].blocked_adomain[1].override")
+	assert.Empty(t, c.Attributes.Badv.ActionOverrides.BlockedAdomain[1].Override.Ids, "attributes.badv.action_overrides[0].blocked_adomain[1].override")
+	assert.Equal(t, []string{"a.com", "b.com", "c.com", "d.com", "e.com"}, c.Attributes.Badv.ActionOverrides.BlockedAdomain[1].Override.Names, "attributes.badv.action_overrides[0].blocked_adomain[1].override")
+
+	assert.Equal(t, []string{"bidderA"}, c.Attributes.Badv.ActionOverrides.BlockUnknownAdomain[0].Conditions.Bidders, "attributes.badv.action_overrides[0].block_unknown_adomain[0].conditions.bidders")
+	assert.Equal(t, []string{"video"}, c.Attributes.Badv.ActionOverrides.BlockUnknownAdomain[0].Conditions.MediaTypes, "attributes.badv.action_overrides[0].block_unknown_adomain[0].conditions.media_types")
+	assert.True(t, c.Attributes.Badv.ActionOverrides.BlockUnknownAdomain[0].Override.IsActive, "attributes.badv.action_overrides[0].block_unknown_adomain[0].override")
+	assert.Empty(t, c.Attributes.Badv.ActionOverrides.BlockUnknownAdomain[0].Override.Ids, "attributes.badv.action_overrides[0].block_unknown_adomain[0].override")
+	assert.Empty(t, c.Attributes.Badv.ActionOverrides.BlockUnknownAdomain[0].Override.Names, "attributes.badv.action_overrides[0].block_unknown_adomain[0].override")
+
+	assert.Equal(t, []string{"12345678"}, c.Attributes.Badv.ActionOverrides.AllowedAdomainForDeals[0].Conditions.DealIds, "attributes.badv.action_overrides[0].allowed_adomain_for_deals[0].conditions.deal_ids")
+	assert.False(t, c.Attributes.Badv.ActionOverrides.AllowedAdomainForDeals[0].Override.IsActive, "attributes.badv.action_overrides[0].allowed_adomain_for_deals[0].override")
+	assert.Empty(t, c.Attributes.Badv.ActionOverrides.AllowedAdomainForDeals[0].Override.Ids, "attributes.badv.action_overrides[0].allowed_adomain_for_deals[0].override")
+	assert.Equal(t, []string{"a.com"}, c.Attributes.Badv.ActionOverrides.AllowedAdomainForDeals[0].Override.Names, "attributes.badv.action_overrides[0].allowed_adomain_for_deals[0].override")
+
+	// bcat
+	assert.False(t, c.Attributes.Bcat.EnforceBlocks, "attributes.bcat.enforce_blocks")
+	assert.False(t, c.Attributes.Bcat.BlockUnknownAdvCat, "attributes.bcat.block_unknown_adv_cat")
+	assert.Equal(t, adcom1.CategoryTaxonomy(6), c.Attributes.Bcat.CategoryTaxonomy, "attributes.bcat.category_taxonomy")
+	assert.Equal(t, []string{"IAB-1", "IAB-2"}, c.Attributes.Bcat.BlockedAdvCat, "attributes.bcat.blocked_adv_cat")
+	assert.Equal(t, []string{"IAB-1"}, c.Attributes.Bcat.AllowedAdvCatForDeals, "attributes.bcat.allowed_adv_cat_for_deals")
+
+	assert.Equal(t, []string{"video"}, c.Attributes.Bcat.ActionOverrides.BlockedAdvCat[0].Conditions.MediaTypes, "attributes.bcat.action_overrides[0].blocked_adv_cat[0].conditions.media_types")
+	assert.Empty(t, c.Attributes.Bcat.ActionOverrides.BlockedAdvCat[0].Conditions.Bidders, "attributes.bcat.action_overrides[0].blocked_adv_cat[0].conditions.bidders")
+	assert.Empty(t, c.Attributes.Bcat.ActionOverrides.BlockedAdvCat[0].Conditions.DealIds, "attributes.bcat.action_overrides[0].blocked_adv_cat[0].conditions.deal_ids")
+	assert.False(t, c.Attributes.Bcat.ActionOverrides.BlockedAdvCat[0].Override.IsActive, "attributes.bcat.action_overrides[0].blocked_adv_cat[0].override")
+	assert.Empty(t, c.Attributes.Bcat.ActionOverrides.BlockedAdvCat[0].Override.Ids, "attributes.bcat.action_overrides[0].blocked_adv_cat[0].override")
+	assert.Equal(t, []string{"IAB-1", "IAB-2", "IAB-3", "IAB-4"}, c.Attributes.Bcat.ActionOverrides.BlockedAdvCat[0].Override.Names, "attributes.bcat.action_overrides[0].blocked_adv_cat[0].override")
+
+	assert.Equal(t, []string{"bidderA"}, c.Attributes.Bcat.ActionOverrides.EnforceBlocks[0].Conditions.Bidders, "attributes.bcat.action_overrides[0].enforce_blocks[0].conditions.bidders")
+	assert.True(t, c.Attributes.Bcat.ActionOverrides.EnforceBlocks[0].Override.IsActive, "attributes.bcat.action_overrides[0].enforce_blocks[0].override")
+	assert.Empty(t, c.Attributes.Bcat.ActionOverrides.EnforceBlocks[0].Override.Ids, "attributes.bcat.action_overrides[0].enforce_blocks[0].override")
+	assert.Empty(t, c.Attributes.Bcat.ActionOverrides.EnforceBlocks[0].Override.Names, "attributes.bcat.action_overrides[0].enforce_blocks[0].override")
+
+	assert.Equal(t, []string{"video"}, c.Attributes.Bcat.ActionOverrides.BlockUnknownAdvCat[0].Conditions.MediaTypes, "attributes.bcat.action_overrides[0].block_unknown_adv_cat[0].conditions.media_types")
+	assert.True(t, c.Attributes.Bcat.ActionOverrides.BlockUnknownAdvCat[0].Override.IsActive, "attributes.bcat.action_overrides[0].block_unknown_adv_cat[0].override")
+	assert.Empty(t, c.Attributes.Bcat.ActionOverrides.BlockUnknownAdvCat[0].Override.Ids, "attributes.bcat.action_overrides[0].block_unknown_adv_cat[0].override")
+	assert.Empty(t, c.Attributes.Bcat.ActionOverrides.BlockUnknownAdvCat[0].Override.Names, "attributes.bcat.action_overrides[0].block_unknown_adv_cat[0].override")
+
+	assert.Equal(t, []string{"1111111"}, c.Attributes.Bcat.ActionOverrides.AllowedAdvCatForDeals[0].Conditions.DealIds, "attributes.bcat.action_overrides[0].allowed_adv_cat_for_deals[0].conditions.deal_ids")
+	assert.False(t, c.Attributes.Bcat.ActionOverrides.AllowedAdvCatForDeals[0].Override.IsActive, "attributes.bcat.action_overrides[0].allowed_adv_cat_for_deals[0].override")
+	assert.Empty(t, c.Attributes.Bcat.ActionOverrides.AllowedAdvCatForDeals[0].Override.Ids, "attributes.bcat.action_overrides[0].allowed_adv_cat_for_deals[0].override")
+	assert.Equal(t, []string{"IAB-1"}, c.Attributes.Bcat.ActionOverrides.AllowedAdvCatForDeals[0].Override.Names, "attributes.bcat.action_overrides[0].allowed_adv_cat_for_deals[0].override")
+
+	// bapp
+	assert.False(t, c.Attributes.Bapp.EnforceBlocks, "attributes.bapp.enforce_blocks")
+	assert.Equal(t, []string{"app1", "app2"}, c.Attributes.Bapp.BlockedApp, "attributes.bapp.blocked_app")
+	assert.Empty(t, c.Attributes.Bapp.AllowedAppForDeals, "attributes.bapp.allowed_app_for_deals")
+
+	assert.Empty(t, c.Attributes.Bapp.ActionOverrides.AllowedAppForDeals, "attributes.bapp.action_overrides[0].allowed_app_for_deals")
+	assert.Empty(t, c.Attributes.Bapp.ActionOverrides.EnforceBlocks, "attributes.bapp.action_overrides[0].enforce_blocks")
+
+	assert.Equal(t, []string{"bidderA"}, c.Attributes.Bapp.ActionOverrides.BlockedApp[0].Conditions.Bidders, "attributes.bapp.action_overrides[0].blocked_app[0].conditions.bidders")
+	assert.False(t, c.Attributes.Bapp.ActionOverrides.BlockedApp[0].Override.IsActive, "attributes.bapp.action_overrides[0].blocked_app[0].override")
+	assert.Empty(t, c.Attributes.Bapp.ActionOverrides.BlockedApp[0].Override.Ids, "attributes.bapp.action_overrides[0].blocked_app[0].override")
+	assert.Equal(t, []string{"app3"}, c.Attributes.Bapp.ActionOverrides.BlockedApp[0].Override.Names, "attributes.bapp.action_overrides[0].blocked_app[0].override")
+
+	// btype
+	assert.Equal(t, []int{3, 4}, c.Attributes.Btype.BlockedBannerType, "attributes.btype.blocked_banner_type")
+
+	assert.Equal(t, []string{"bidderA"}, c.Attributes.Btype.ActionOverrides.BlockedBannerType[0].Conditions.Bidders, "attributes.btype.action_overrides[0].blocked_banner_type[0].conditions.bidders")
+	assert.Equal(t, []int{3, 4, 5}, c.Attributes.Btype.ActionOverrides.BlockedBannerType[0].Override.Ids, "attributes.btype.action_overrides[0].blocked_banner_type[0].override")
+	assert.Empty(t, c.Attributes.Btype.ActionOverrides.BlockedBannerType[0].Override.Names, "attributes.btype.action_overrides[0].blocked_banner_type[0].override")
+	assert.False(t, c.Attributes.Btype.ActionOverrides.BlockedBannerType[0].Override.IsActive, "attributes.btype.action_overrides[0].blocked_banner_type[0].override")
+
+	// battr
+	assert.Empty(t, c.Attributes.Battr.AllowedBannerAttrForDeals, "attributes.battr.allowed_banner_attr_for_deals")
+	assert.False(t, c.Attributes.Battr.EnforceBlocks, "attributes.battr.enforce_blocks")
+	assert.Equal(t, []int{1, 8, 9, 10}, c.Attributes.Battr.BlockedBannerAttr, "attributes.battr.blocked_banner_attr")
+
+	assert.Empty(t, c.Attributes.Battr.ActionOverrides.AllowedBannerAttrForDeals, "attributes.battr.action_overrides[0].allowed_banner_attr_for_deals")
+	assert.Empty(t, c.Attributes.Battr.ActionOverrides.BlockedBannerAttr, "attributes.battr.action_overrides[0].blocked_banner_attr")
+
+	assert.Equal(t, []string{"bidderA"}, c.Attributes.Battr.ActionOverrides.EnforceBlocks[0].Conditions.Bidders, "attributes.battr.action_overrides[0].enforce_blocks[0].conditions.bidders")
+	assert.True(t, c.Attributes.Battr.ActionOverrides.EnforceBlocks[0].Override.IsActive, "attributes.battr.action_overrides[0].enforce_blocks[0].override")
+	assert.Empty(t, c.Attributes.Battr.ActionOverrides.EnforceBlocks[0].Override.Ids, "attributes.battr.action_overrides[0].enforce_blocks[0].override")
+	assert.Empty(t, c.Attributes.Battr.ActionOverrides.EnforceBlocks[0].Override.Names, "attributes.battr.action_overrides[0].enforce_blocks[0].override")
+}
+
+func TestOverride_UnmarshalJSON(t *testing.T) {
+	// error on invalid JSON
+	override := Override{}
+	assert.Error(t, override.UnmarshalJSON([]byte("...")), "Error expected on invalid JSON.")
+
+	// expect IsActive to be initialized correctly
+	override = Override{}
+	assert.NoError(t, override.UnmarshalJSON([]byte("true")), "Failed to unmarshal bool override.")
+	assert.Equal(t, Override{IsActive: true}, override, "Override.IsActive expected to be true.")
+
+	// expect IDs to be initialized correctly
+	override = Override{}
+	assert.NoError(t, override.UnmarshalJSON([]byte("[1, 2, 3]")), "Failed to unmarshal override with IDs.")
+	assert.Equal(t, Override{Ids: []int{1, 2, 3}}, override, "Invalid override.IDs.")
+
+	// expect Names to be initialized correctly
+	override = Override{}
+	assert.NoError(t, override.UnmarshalJSON([]byte(`["one", "two"]`)), "Failed to unmarshal override with Names.")
+	assert.Equal(t, Override{Names: []string{"one", "two"}}, override, "Invalid override.Names.")
+
+	// expect empty override on ignored JSON
+	override = Override{}
+	assert.NoError(t, override.UnmarshalJSON([]byte(`"string"`)), "Failed to unmarshal override with ignored value.")
+	assert.Equal(t, Override{}, override, "Empty override expected.")
+}
diff --git a/openrtb_ext/request_wrapper_test.go b/openrtb_ext/request_wrapper_test.go
index cb4aef21..110eb2df 100644
--- a/openrtb_ext/request_wrapper_test.go
+++ b/openrtb_ext/request_wrapper_test.go
@@ -917,6 +917,24 @@ func TestImpWrapperRebuildImp(t *testing.T) {
 			impExtWrapper: ImpExt{prebid: &ExtImpPrebid{IsRewardedInventory: nil}, prebidDirty: true},
 			expectedImp:   openrtb2.Imp{},
 		},
+		{
+			description:   "Populated Tid - Dirty",
+			imp:           openrtb2.Imp{Ext: json.RawMessage(`{"tid": "some-tid"}`)},
+			impExtWrapper: ImpExt{tidDirty: true, tid: "12345"},
+			expectedImp:   openrtb2.Imp{Ext: json.RawMessage(`{"tid":"12345"}`)},
+		},
+		{
+			description:   "Populated Tid - Dirty - No Change",
+			imp:           openrtb2.Imp{Ext: json.RawMessage(`{"tid": "some-tid"}`)},
+			impExtWrapper: ImpExt{tid: "some-tid", tidDirty: true},
+			expectedImp:   openrtb2.Imp{Ext: json.RawMessage(`{"tid":"some-tid"}`)},
+		},
+		{
+			description:   "Populated Tid - Dirty - Cleared",
+			imp:           openrtb2.Imp{Ext: json.RawMessage(`{"tid":"some-tid"}`)},
+			impExtWrapper: ImpExt{tid: "", tidDirty: true},
+			expectedImp:   openrtb2.Imp{},
+		},
 	}
 
 	for _, test := range testCases {
@@ -945,12 +963,14 @@ func TestImpWrapperGetImpExt(t *testing.T) {
 		},
 		{
 			description:  "Populated - Ext",
-			givenWrapper: ImpWrapper{Imp: &openrtb2.Imp{Ext: json.RawMessage(`{"prebid":{"is_rewarded_inventory":1},"other":42}`)}},
+			givenWrapper: ImpWrapper{Imp: &openrtb2.Imp{Ext: json.RawMessage(`{"prebid":{"is_rewarded_inventory":1},"other":42, "tid": "test-tid"}`)}},
 			expectedImpExt: ImpExt{
 				ext: map[string]json.RawMessage{
 					"prebid": json.RawMessage(`{"is_rewarded_inventory":1}`),
 					"other":  json.RawMessage(`42`),
+					"tid":    json.RawMessage(`"test-tid"`),
 				},
+				tid:    "test-tid",
 				prebid: &ExtImpPrebid{IsRewardedInventory: &isRewardedInventoryOne},
 			},
 		},
@@ -983,3 +1003,16 @@ func TestImpWrapperGetImpExt(t *testing.T) {
 		}
 	}
 }
+
+func TestImpExtTid(t *testing.T) {
+	impExt := &ImpExt{}
+
+	impExt.unmarshal(nil)
+	assert.Equal(t, false, impExt.Dirty(), "New impext should not be dirty.")
+	assert.Empty(t, impExt.GetTid(), "Empty ImpExt should have  empty tid")
+
+	newTid := "tid"
+	impExt.SetTid(newTid)
+	assert.Equal(t, "tid", impExt.GetTid(), "ImpExt tid is incorrect")
+	assert.Equal(t, true, impExt.Dirty(), "New impext should be dirty.")
+}