Repository: prebid/prebid-server
Pull request #1304 (2020-05-17 16:47:05)

privacy: Potential JSON injection
`privacy/ccpa/policy.go` and `privacy/gdpr/policy.go` include code that attempts to generate JSON by concatenating strings. If the `us_privacy` or `consent` fields contain the character `"`, this can produce malformed JSON, or JSON with an unintended structure.

This PR fixes the problem by encoding all JSON using the `json` module.

The included tests demonstrate that an attacker could exploit this bug to trick Prebid Server into mutating an OpenRTB bid request to contain arbitrary data, bypassing validation. These tests fail against the existing code.

SyntaxNode: Thank you reviewing the project for potential security risks. I greatly appreciate the scrutiny to ensure Prebid Server doesn't provide attack vectors which put our customers at risk.

In isolation, I agree this method poses a security risk. I don't see it as a possible attack vector though due to two reasons:
1) This method is only called for AMP requests, in which the consent string is validated as either a CCPA or GDPR TCF1/TCF2 consent string. 
2) Go's `json.Marshal` method validates embedded json to avoid injection attacks such as this. See: https://play.golang.org/p/gumbqihqXJ6

The benefit from this change is that we are protected from potentially harmful future uses of this method or if the Go json marshaller changes in the future and no longer properly validates embedded json.

The cost is performance. Use of the `json.Marshal` method takes 3x longer.
```
BenchmarkWrite-8      	 3492728	       359 ns/op	      32 B/op	       1 allocs/op
BenchmarkWriteNew-8   	 1000000	      1021 ns/op	      64 B/op	       2 allocs/op
```

Thoughts?
djcsdy: @SyntaxNode Agreed that it is not possible to inject JSON outside the `ext` subtrees, which is where the most potential for mischief lies.

It *is* possible to inject arbitrary JSON inside the `user.ext` or `regs.ext` subtrees. I can’t think of a way to exploit this with OpenRTB extensions that I’m aware of, but it could easily become exploitable without notice.

IMHO it is almost never a good idea to construct structured data by concatenating strings (of characters or bytes) outside of a specialised serializer or marshaller. IME even many programmers with decades of experience do not understand how badly this approach can go wrong.

Programmers use the code that already exists in a project as an exemplar of how to write new code, so if you use a particular approach to solve a problem, the project as a whole is likely to use a similar approach to solve similar problems in the future. So although this particular code might be safe (which is already a wobbly assumption), it will encourage similar unsafe code to creep in in future. I did a quick search of the project when I proposed this change and found no other instances of code that constructs JSON by string concatenation, so this is a chance to nip a potential future problem in the bud.

FWIW I am still not particularly happy with this code as an exemplar of JSON-handling code even after my changes. It still uses string concatenation, it’s just more careful to ensure every component is properly encoded before concatenation. There is probably a better way to write this in terms of JSON-handling correctness and clarity. I went with the minimal change that solved the problem.

Everything stated here is just my opinion. It’s up to you to decide if the performance difference is worth the risk.

Patch:
diff --git a/adapters/adhese/adhese.go b/adapters/adhese/adhese.go
new file mode 100644
index 00000000..10aaf6e8
--- /dev/null
+++ b/adapters/adhese/adhese.go
@@ -0,0 +1,269 @@
+package adhese
+
+import (
+	"encoding/json"
+	"fmt"
+	"net/http"
+	"net/url"
+	"sort"
+	"strconv"
+	"strings"
+	"text/template"
+
+	"github.com/golang/glog"
+	"github.com/mxmCherry/openrtb"
+	"github.com/prebid/prebid-server/adapters"
+	"github.com/prebid/prebid-server/errortypes"
+	"github.com/prebid/prebid-server/macros"
+	"github.com/prebid/prebid-server/openrtb_ext"
+)
+
+type AdheseAdapter struct {
+	endpointTemplate template.Template
+}
+
+func extractSlotParameter(parameters openrtb_ext.ExtImpAdhese) string {
+	return fmt.Sprintf("/sl%s-%s", url.PathEscape(parameters.Location), url.PathEscape(parameters.Format))
+}
+
+func extractTargetParameters(parameters openrtb_ext.ExtImpAdhese) string {
+	if len(parameters.Keywords) == 0 {
+		return ""
+	}
+	var parametersAsString = ""
+	var targetParsed map[string]interface{}
+	err := json.Unmarshal(parameters.Keywords, &targetParsed)
+	if err != nil {
+		return ""
+	}
+
+	targetKeys := make([]string, 0, len(targetParsed))
+	for key := range targetParsed {
+		targetKeys = append(targetKeys, key)
+	}
+	sort.Strings(targetKeys)
+
+	for _, targetKey := range targetKeys {
+		var targetingValues = targetParsed[targetKey].([]interface{})
+		parametersAsString += "/" + url.PathEscape(targetKey)
+		for _, targetRawValKey := range targetingValues {
+			var targetValueParsed = targetRawValKey.(string)
+			parametersAsString += targetValueParsed + ";"
+		}
+		parametersAsString = strings.TrimRight(parametersAsString, ";")
+	}
+
+	return parametersAsString
+}
+
+func extractGdprParameter(request *openrtb.BidRequest) string {
+	if request.User != nil {
+		var extUser openrtb_ext.ExtUser
+		if err := json.Unmarshal(request.User.Ext, &extUser); err == nil {
+			return "/xt" + extUser.Consent
+		}
+	}
+	return ""
+}
+
+func extractRefererParameter(request *openrtb.BidRequest) string {
+	if request.Site != nil && request.Site.Page != "" {
+		return "/xf" + url.QueryEscape(request.Site.Page)
+	}
+	return ""
+}
+
+func (a *AdheseAdapter) MakeRequests(request *openrtb.BidRequest, reqInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) {
+	errs := make([]error, 0, len(request.Imp))
+
+	var err error
+
+	// If all the requests are invalid, Call to adaptor is skipped
+	if len(request.Imp) == 0 {
+		errs = append(errs, WrapReqError("Imp is empty"))
+		return nil, errs
+	}
+
+	var imp = &request.Imp[0]
+	var bidderExt adapters.ExtImpBidder
+
+	if err := json.Unmarshal(imp.Ext, &bidderExt); err != nil {
+		errs = append(errs, WrapReqError("Request could not be parsed as ExtImpBidder due to: "+err.Error()))
+		return nil, errs
+	}
+
+	var params openrtb_ext.ExtImpAdhese
+	if err := json.Unmarshal(bidderExt.Bidder, &params); err != nil {
+		errs = append(errs, WrapReqError("Request could not be parsed as ExtImpAdhese due to: "+err.Error()))
+		return nil, errs
+	}
+
+	// Compose url
+	endpointParams := macros.EndpointTemplateParams{AccountID: params.Account}
+
+	host, err := macros.ResolveMacros(*&a.endpointTemplate, endpointParams)
+	if err != nil {
+		errs = append(errs, WrapReqError("Could not compose url from template and request account val: "+err.Error()))
+		return nil, errs
+	}
+	complete_url := fmt.Sprintf("%s%s%s%s%s",
+		host,
+		extractSlotParameter(params),
+		extractTargetParameters(params),
+		extractGdprParameter(request),
+		extractRefererParameter(request))
+
+	return []*adapters.RequestData{{
+		Method: "GET",
+		Uri:    complete_url,
+	}}, errs
+}
+
+func (a *AdheseAdapter) MakeBids(internalRequest *openrtb.BidRequest, externalRequest *adapters.RequestData, response *adapters.ResponseData) (*adapters.BidderResponse, []error) {
+	if response.StatusCode == http.StatusNoContent {
+		return nil, nil
+	} else if response.StatusCode != http.StatusOK {
+		return nil, []error{WrapServerError(fmt.Sprintf("Unexpected status code: %d.", response.StatusCode))}
+	}
+
+	var bidResponse openrtb.BidResponse
+
+	var adheseBidResponseArray []AdheseBid
+	if err := json.Unmarshal(response.Body, &adheseBidResponseArray); err != nil {
+		return nil, []error{err, WrapServerError(fmt.Sprintf("Response %v could not be parsed as generic Adhese bid.", string(response.Body)))}
+	}
+
+	var adheseBid = adheseBidResponseArray[0]
+
+	if adheseBid.Origin == "JERLICIA" {
+		var extArray []AdheseExt
+		var originDataArray []AdheseOriginData
+		if err := json.Unmarshal(response.Body, &extArray); err != nil {
+			return nil, []error{err, WrapServerError(fmt.Sprintf("Response %v could not be parsed to JERLICIA ext.", string(response.Body)))}
+		}
+
+		if err := json.Unmarshal(response.Body, &originDataArray); err != nil {
+			return nil, []error{err, WrapServerError(fmt.Sprintf("Response %v could not be parsed to JERLICIA origin data.", string(response.Body)))}
+		}
+		bidResponse = convertAdheseBid(adheseBid, extArray[0], originDataArray[0])
+	} else {
+		bidResponse = convertAdheseOpenRtbBid(adheseBid)
+	}
+
+	price, err := strconv.ParseFloat(adheseBid.Extension.Prebid.Cpm.Amount, 64)
+	if err != nil {
+		return nil, []error{err, WrapServerError(fmt.Sprintf("Could not parse Price %v as float ", string(adheseBid.Extension.Prebid.Cpm.Amount)))}
+	}
+	width, err := strconv.ParseUint(adheseBid.Width, 10, 64)
+	if err != nil {
+		return nil, []error{err, WrapServerError(fmt.Sprintf("Could not parse Width %v as int ", string(adheseBid.Width)))}
+	}
+	height, err := strconv.ParseUint(adheseBid.Height, 10, 64)
+	if err != nil {
+		return nil, []error{err, WrapServerError(fmt.Sprintf("Could not parse Height %v as int ", string(adheseBid.Height)))}
+	}
+	bidResponse.Cur = adheseBid.Extension.Prebid.Cpm.Currency
+	if len(bidResponse.SeatBid) > 0 && len(bidResponse.SeatBid[0].Bid) > 0 {
+		bidResponse.SeatBid[0].Bid[0].Price = price
+		bidResponse.SeatBid[0].Bid[0].W = width
+		bidResponse.SeatBid[0].Bid[0].H = height
+	}
+
+	bidderResponse := adapters.NewBidderResponseWithBidsCapacity(5)
+
+	if len(bidResponse.SeatBid) == 0 {
+		return nil, []error{WrapServerError("Response resulted in an empty seatBid array.")}
+	}
+
+	var errs []error
+	for _, sb := range bidResponse.SeatBid {
+		for i := 0; i < len(sb.Bid); i++ {
+			bid := sb.Bid[i]
+			bidderResponse.Bids = append(bidderResponse.Bids, &adapters.TypedBid{
+				Bid:     &bid,
+				BidType: getBidType(bid.AdM),
+			})
+
+		}
+	}
+	return bidderResponse, errs
+}
+
+func convertAdheseBid(adheseBid AdheseBid, adheseExt AdheseExt, adheseOriginData AdheseOriginData) openrtb.BidResponse {
+	adheseExtJson, err := json.Marshal(adheseOriginData)
+	if err != nil {
+		glog.Error(fmt.Sprintf("Unable to parse adhese Origin Data as JSON due to %v", err))
+		adheseExtJson = make([]byte, 0)
+	}
+	return openrtb.BidResponse{
+		ID: adheseExt.Id,
+		SeatBid: []openrtb.SeatBid{{
+			Bid: []openrtb.Bid{{
+				DealID: adheseExt.OrderId,
+				CrID:   adheseExt.Id,
+				AdM:    getAdMarkup(adheseBid, adheseExt),
+				Ext:    adheseExtJson,
+			}},
+			Seat: "",
+		}},
+	}
+}
+
+func convertAdheseOpenRtbBid(adheseBid AdheseBid) openrtb.BidResponse {
+	var response openrtb.BidResponse = adheseBid.OriginData
+	if len(response.SeatBid) > 0 && len(response.SeatBid[0].Bid) > 0 {
+		response.SeatBid[0].Bid[0].AdM = adheseBid.Body
+	}
+	return response
+}
+
+func getAdMarkup(adheseBid AdheseBid, adheseExt AdheseExt) string {
+	if adheseExt.Ext == "js" {
+		if ContainsAny(adheseBid.Body, []string{"<script", "<div", "<html"}) {
+			counter := ""
+			if len(adheseExt.ImpressionCounter) > 0 {
+				counter = "<img src='" + adheseExt.ImpressionCounter + "' style='height:1px; width:1px; margin: -1px -1px; display:none;'/>"
+			}
+			return adheseBid.Body + counter
+		}
+		if ContainsAny(adheseBid.Body, []string{"<?xml", "<vast"}) {
+			return adheseBid.Body
+		}
+	}
+	return adheseExt.Tag
+}
+
+func getBidType(bidAdm string) openrtb_ext.BidType {
+	if bidAdm != "" && ContainsAny(bidAdm, []string{"<?xml", "<vast"}) {
+		return openrtb_ext.BidTypeVideo
+	}
+	return openrtb_ext.BidTypeBanner
+}
+
+func WrapReqError(errorStr string) *errortypes.BadInput {
+	return &errortypes.BadInput{Message: errorStr}
+}
+
+func WrapServerError(errorStr string) *errortypes.BadServerResponse {
+	return &errortypes.BadServerResponse{Message: errorStr}
+}
+
+func ContainsAny(raw string, keys []string) bool {
+	lowerCased := strings.ToLower(raw)
+	for i := 0; i < len(keys); i++ {
+		if strings.Contains(lowerCased, keys[i]) {
+			return true
+		}
+	}
+	return false
+
+}
+
+func NewAdheseBidder(uri string) *AdheseAdapter {
+	template, err := template.New("endpointTemplate").Parse(uri)
+	if err != nil {
+		glog.Fatal("Unable to parse endpoint url template")
+		return nil
+	}
+	return &AdheseAdapter{endpointTemplate: *template}
+}
diff --git a/adapters/adhese/utils.go b/adapters/adhese/utils.go
new file mode 100644
index 00000000..9983be96
--- /dev/null
+++ b/adapters/adhese/utils.go
@@ -0,0 +1,45 @@
+package adhese
+
+import "github.com/mxmCherry/openrtb"
+
+type AdheseOriginData struct {
+	Priority                  string `json:"priority"`
+	OrderProperty             string `json:"orderProperty"`
+	AdFormat                  string `json:"adFormat"`
+	AdType                    string `json:"adType"`
+	AdspaceId                 string `json:"adspaceId"`
+	LibId                     string `json:"libId"`
+	SlotID                    string `json:"slotID,omitempty"`
+	ViewableImpressionCounter string `json:"viewableImpressionCounter"`
+}
+
+type AdheseExt struct {
+	Id                string `json:"id"`
+	OrderId           string `json:"orderId"`
+	ImpressionCounter string `json:"impressionCounter"`
+	Tag               string `json:"tag,omitempty"`
+	Ext               string `json:"ext"`
+}
+
+type AdheseBid struct {
+	Origin         string              `json:"origin"`
+	OriginData     openrtb.BidResponse `json:"originData"`
+	OriginInstance string              `json:"originInstance,omitempty"`
+	Body           string              `json:"body,omitempty"`
+	Height         string              `json:"height"`
+	Width          string              `json:"width"`
+	Extension      Prebid              `json:"extension"`
+}
+
+type Prebid struct {
+	Prebid CPM `json:"prebid"`
+}
+
+type CPM struct {
+	Cpm CPMValues `json:"cpm"`
+}
+
+type CPMValues struct {
+	Amount   string `json:"amount"`
+	Currency string `json:"currency"`
+}
diff --git a/adapters/advangelists/usersync.go b/adapters/advangelists/usersync.go
index 6167a2e3..10c46d18 100644
--- a/adapters/advangelists/usersync.go
+++ b/adapters/advangelists/usersync.go
@@ -8,5 +8,5 @@ import (
 )
 
 func NewAdvangelistsSyncer(temp *template.Template) usersync.Usersyncer {
-	return adapters.NewSyncer("advangelists", 61, temp, adapters.SyncTypeIframe)
+	return adapters.NewSyncer("advangelists", 0, temp, adapters.SyncTypeIframe)
 }
diff --git a/adapters/consumable/consumable.go b/adapters/consumable/consumable.go
index d3954104..1fa23377 100644
--- a/adapters/consumable/consumable.go
+++ b/adapters/consumable/consumable.go
@@ -7,6 +7,7 @@ import (
 	"github.com/prebid/prebid-server/adapters"
 	"github.com/prebid/prebid-server/errortypes"
 	"github.com/prebid/prebid-server/openrtb_ext"
+	"github.com/prebid/prebid-server/privacy/ccpa"
 	"net/http"
 	"net/url"
 	"strconv"
@@ -32,6 +33,8 @@ type bidRequest struct {
 	Url                string      `json:"url,omitempty"`
 	EnableBotFiltering bool        `json:"enableBotFiltering,omitempty"`
 	Parallel           bool        `json:"parallel"`
+	CCPA               string      `json:"ccpa,omitempty"`
+	GDPR               *bidGdpr    `json:"gdpr,omitempty"`
 }
 
 type placement struct {
@@ -47,6 +50,11 @@ type user struct {
 	Key string `json:"key,omitempty"`
 }
 
+type bidGdpr struct {
+	Applies *bool  `json:"applies,omitempty"`
+	Consent string `json:"consent,omitempty"`
+}
+
 type bidResponse struct {
 	Decisions map[string]decision `json:"decisions"` // map by bidId
 }
@@ -124,6 +132,34 @@ func (a *ConsumableAdapter) MakeRequests(request *openrtb.BidRequest, reqInfo *a
 		body.Url = request.Site.Page     // where the impression will be made
 	}
 
+	gdpr := bidGdpr{}
+
+	ccpaPolicy, err := ccpa.ReadPolicy(request)
+	if err == nil {
+		body.CCPA = ccpaPolicy.Value
+	}
+
+	// TODO: Replace with gdpr.ReadPolicy when it is available
+	if request.Regs != nil && request.Regs.Ext != nil {
+		var extRegs openrtb_ext.ExtRegs
+		if err := json.Unmarshal(request.Regs.Ext, &extRegs); err == nil {
+			if extRegs.GDPR != nil {
+				applies := *extRegs.GDPR != 0
+				gdpr.Applies = &applies
+				body.GDPR = &gdpr
+			}
+		}
+	}
+
+	// TODO: Replace with gdpr.ReadPolicy when it is available
+	if request.User != nil && request.User.Ext != nil {
+		var extUser openrtb_ext.ExtUser
+		if err := json.Unmarshal(request.User.Ext, &extUser); err == nil {
+			gdpr.Consent = extUser.Consent
+			body.GDPR = &gdpr
+		}
+	}
+
 	for i, impression := range request.Imp {
 
 		_, consumableExt, err := extractExtensions(impression)
diff --git a/adapters/consumable/consumable/supplemental/simple-banner-gdpr-2.json b/adapters/consumable/consumable/supplemental/simple-banner-gdpr-2.json
new file mode 100644
index 00000000..32c3fd6e
--- /dev/null
+++ b/adapters/consumable/consumable/supplemental/simple-banner-gdpr-2.json
@@ -0,0 +1,127 @@
+{
+  "mockBidRequest": {
+    "id": "test-request-id",
+    "imp": [
+      {
+        "id": "test-imp-id",
+        "banner": {
+          "format": [{"w": 728, "h": 250}]
+        },
+        "ext": {
+          "bidder": {
+            "networkId": 11,
+            "siteId": 32,
+            "unitId": 42
+          }
+        }
+      }
+    ],
+    "device": {
+      "ua": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.91 Safari/537.36",
+      "ip": "123.123.123.123"
+    },
+    "site": {
+      "domain": "www.some.com",
+      "page": "http://www.some.com/page-where-ad-will-be-shown"
+    },
+    "regs": {
+      "ext": {
+        "gdpr": 0
+      }
+    }
+  },
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "https://e.serverbid.com/api/v2",
+        "headers": {
+          "Accept": [
+            "application/json"
+          ],
+          "Content-Type": [
+            "application/json"
+          ],
+          "User-Agent": [
+            "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.91 Safari/537.36"
+          ],
+          "X-Forwarded-For": [
+            "123.123.123.123"
+          ],
+          "Forwarded": [
+            "for=123.123.123.123"
+          ],
+          "Origin": [
+            "http://www.some.com"
+          ],
+          "Referer": [
+            "http://www.some.com/page-where-ad-will-be-shown"
+          ]
+        },
+        "body": {
+          "placements": [
+            {
+              "adTypes": [2730],
+              "divName": "test-imp-id",
+              "networkId": 11,
+              "siteId": 32,
+              "unitId": 42
+            }
+          ],
+          "networkId": 11,
+          "siteId": 32,
+          "unitId": 42,
+          "time": 1451651415,
+          "url": "http://www.some.com/page-where-ad-will-be-shown",
+          "includePricingData": true,
+          "user":{},
+          "enableBotFiltering": true,
+          "parallel": true,
+          "gdpr": {
+            "applies": false
+          }
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "decisions": {
+            "test-imp-id": {
+              "adId": 1234567890,
+              "pricing": {
+                "clearPrice": 0.5
+              },
+              "width": 728,
+              "height": 250,
+              "impressionUrl": "http://localhost:8080/shown",
+              "contents" : [
+                {
+                  "body": "<blink>Remember this: https://www.google.com/search?q=blink+tag ?</blink>"
+                }
+              ]
+            }
+          }
+        }
+      }
+    }
+  ],
+  "expectedBidResponses": [
+    {
+      "currency": "USD",
+      "bids": [
+        {
+          "bid": {
+            "id": "test-request-id",
+            "impid": "test-imp-id",
+            "price": 0.5,
+            "adm": "<blink>Remember this: https://www.google.com/search?q=blink+tag ?</blink>",
+            "crid": "1234567890",
+            "exp": 30,
+            "w": 728,
+            "h": 250
+          },
+          "type": "banner"
+        }
+      ]
+    }
+  ]
+}
diff --git a/adapters/consumable/consumable/supplemental/simple-banner-gdpr-3.json b/adapters/consumable/consumable/supplemental/simple-banner-gdpr-3.json
new file mode 100644
index 00000000..afbb7e2b
--- /dev/null
+++ b/adapters/consumable/consumable/supplemental/simple-banner-gdpr-3.json
@@ -0,0 +1,127 @@
+{
+  "mockBidRequest": {
+    "id": "test-request-id",
+    "imp": [
+      {
+        "id": "test-imp-id",
+        "banner": {
+          "format": [{"w": 728, "h": 250}]
+        },
+        "ext": {
+          "bidder": {
+            "networkId": 11,
+            "siteId": 32,
+            "unitId": 42
+          }
+        }
+      }
+    ],
+    "device": {
+      "ua": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.91 Safari/537.36",
+      "ip": "123.123.123.123"
+    },
+    "site": {
+      "domain": "www.some.com",
+      "page": "http://www.some.com/page-where-ad-will-be-shown"
+    },
+    "user": {
+      "ext": {
+        "consent": "abcdefghijklm"
+      }
+    }
+  },
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "https://e.serverbid.com/api/v2",
+        "headers": {
+          "Accept": [
+            "application/json"
+          ],
+          "Content-Type": [
+            "application/json"
+          ],
+          "User-Agent": [
+            "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.91 Safari/537.36"
+          ],
+          "X-Forwarded-For": [
+            "123.123.123.123"
+          ],
+          "Forwarded": [
+            "for=123.123.123.123"
+          ],
+          "Origin": [
+            "http://www.some.com"
+          ],
+          "Referer": [
+            "http://www.some.com/page-where-ad-will-be-shown"
+          ]
+        },
+        "body": {
+          "placements": [
+            {
+              "adTypes": [2730],
+              "divName": "test-imp-id",
+              "networkId": 11,
+              "siteId": 32,
+              "unitId": 42
+            }
+          ],
+          "networkId": 11,
+          "siteId": 32,
+          "unitId": 42,
+          "time": 1451651415,
+          "url": "http://www.some.com/page-where-ad-will-be-shown",
+          "includePricingData": true,
+          "user":{},
+          "enableBotFiltering": true,
+          "parallel": true,
+          "gdpr": {
+            "consent": "abcdefghijklm"
+          }
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "decisions": {
+            "test-imp-id": {
+              "adId": 1234567890,
+              "pricing": {
+                "clearPrice": 0.5
+              },
+              "width": 728,
+              "height": 250,
+              "impressionUrl": "http://localhost:8080/shown",
+              "contents" : [
+                {
+                  "body": "<blink>Remember this: https://www.google.com/search?q=blink+tag ?</blink>"
+                }
+              ]
+            }
+          }
+        }
+      }
+    }
+  ],
+  "expectedBidResponses": [
+    {
+      "currency": "USD",
+      "bids": [
+        {
+          "bid": {
+            "id": "test-request-id",
+            "impid": "test-imp-id",
+            "price": 0.5,
+            "adm": "<blink>Remember this: https://www.google.com/search?q=blink+tag ?</blink>",
+            "crid": "1234567890",
+            "exp": 30,
+            "w": 728,
+            "h": 250
+          },
+          "type": "banner"
+        }
+      ]
+    }
+  ]
+}
diff --git a/adapters/consumable/consumable/supplemental/simple-banner-gdpr.json b/adapters/consumable/consumable/supplemental/simple-banner-gdpr.json
new file mode 100644
index 00000000..1d422e25
--- /dev/null
+++ b/adapters/consumable/consumable/supplemental/simple-banner-gdpr.json
@@ -0,0 +1,133 @@
+{
+  "mockBidRequest": {
+    "id": "test-request-id",
+    "imp": [
+      {
+        "id": "test-imp-id",
+        "banner": {
+          "format": [{"w": 728, "h": 250}]
+        },
+        "ext": {
+          "bidder": {
+            "networkId": 11,
+            "siteId": 32,
+            "unitId": 42
+          }
+        }
+      }
+    ],
+    "device": {
+      "ua": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.91 Safari/537.36",
+      "ip": "123.123.123.123"
+    },
+    "site": {
+      "domain": "www.some.com",
+      "page": "http://www.some.com/page-where-ad-will-be-shown"
+    },
+    "regs": {
+      "ext": {
+        "gdpr": 1
+      }
+    },
+    "user": {
+      "ext": {
+        "consent": "abcdefghijklm"
+      }
+    }
+  },
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "https://e.serverbid.com/api/v2",
+        "headers": {
+          "Accept": [
+            "application/json"
+          ],
+          "Content-Type": [
+            "application/json"
+          ],
+          "User-Agent": [
+            "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.91 Safari/537.36"
+          ],
+          "X-Forwarded-For": [
+            "123.123.123.123"
+          ],
+          "Forwarded": [
+            "for=123.123.123.123"
+          ],
+          "Origin": [
+            "http://www.some.com"
+          ],
+          "Referer": [
+            "http://www.some.com/page-where-ad-will-be-shown"
+          ]
+        },
+        "body": {
+          "placements": [
+            {
+              "adTypes": [2730],
+              "divName": "test-imp-id",
+              "networkId": 11,
+              "siteId": 32,
+              "unitId": 42
+            }
+          ],
+          "networkId": 11,
+          "siteId": 32,
+          "unitId": 42,
+          "time": 1451651415,
+          "url": "http://www.some.com/page-where-ad-will-be-shown",
+          "includePricingData": true,
+          "user":{},
+          "enableBotFiltering": true,
+          "parallel": true,
+          "gdpr": {
+            "applies": true,
+            "consent": "abcdefghijklm"
+          }
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "decisions": {
+            "test-imp-id": {
+              "adId": 1234567890,
+              "pricing": {
+                "clearPrice": 0.5
+              },
+              "width": 728,
+              "height": 250,
+              "impressionUrl": "http://localhost:8080/shown",
+              "contents" : [
+                {
+                  "body": "<blink>Remember this: https://www.google.com/search?q=blink+tag ?</blink>"
+                }
+              ]
+            }
+          }
+        }
+      }
+    }
+  ],
+  "expectedBidResponses": [
+    {
+      "currency": "USD",
+      "bids": [
+        {
+          "bid": {
+            "id": "test-request-id",
+            "impid": "test-imp-id",
+            "price": 0.5,
+            "adm": "<blink>Remember this: https://www.google.com/search?q=blink+tag ?</blink>",
+            "crid": "1234567890",
+            "exp": 30,
+            "w": 728,
+            "h": 250
+          },
+          "type": "banner"
+        }
+      ]
+    }
+  ]
+}
diff --git a/adapters/consumable/consumable/supplemental/simple-banner-us-privacy.json b/adapters/consumable/consumable/supplemental/simple-banner-us-privacy.json
new file mode 100644
index 00000000..6313606e
--- /dev/null
+++ b/adapters/consumable/consumable/supplemental/simple-banner-us-privacy.json
@@ -0,0 +1,125 @@
+{
+  "mockBidRequest": {
+    "id": "test-request-id",
+    "imp": [
+      {
+        "id": "test-imp-id",
+        "banner": {
+          "format": [{"w": 728, "h": 250}]
+        },
+        "ext": {
+          "bidder": {
+            "networkId": 11,
+            "siteId": 32,
+            "unitId": 42
+          }
+        }
+      }
+    ],
+    "device": {
+      "ua": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.91 Safari/537.36",
+      "ip": "123.123.123.123"
+    },
+    "site": {
+      "domain": "www.some.com",
+      "page": "http://www.some.com/page-where-ad-will-be-shown"
+    },
+    "regs": {
+      "ext": {
+        "us_privacy": "1NYN"
+      }
+    }
+  },
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "https://e.serverbid.com/api/v2",
+        "headers": {
+          "Accept": [
+            "application/json"
+          ],
+          "Content-Type": [
+            "application/json"
+          ],
+          "User-Agent": [
+            "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.91 Safari/537.36"
+          ],
+          "X-Forwarded-For": [
+            "123.123.123.123"
+          ],
+          "Forwarded": [
+            "for=123.123.123.123"
+          ],
+          "Origin": [
+            "http://www.some.com"
+          ],
+          "Referer": [
+            "http://www.some.com/page-where-ad-will-be-shown"
+          ]
+        },
+        "body": {
+          "placements": [
+            {
+              "adTypes": [2730],
+              "divName": "test-imp-id",
+              "networkId": 11,
+              "siteId": 32,
+              "unitId": 42
+            }
+          ],
+          "networkId": 11,
+          "siteId": 32,
+          "unitId": 42,
+          "time": 1451651415,
+          "url": "http://www.some.com/page-where-ad-will-be-shown",
+          "includePricingData": true,
+          "user":{},
+          "enableBotFiltering": true,
+          "parallel": true,
+          "ccpa": "1NYN"
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "decisions": {
+            "test-imp-id": {
+              "adId": 1234567890,
+              "pricing": {
+                "clearPrice": 0.5
+              },
+              "width": 728,
+              "height": 250,
+              "impressionUrl": "http://localhost:8080/shown",
+              "contents" : [
+                {
+                  "body": "<blink>Remember this: https://www.google.com/search?q=blink+tag ?</blink>"
+                }
+              ]
+            }
+          }
+        }
+      }
+    }
+  ],
+  "expectedBidResponses": [
+    {
+      "currency": "USD",
+      "bids": [
+        {
+          "bid": {
+            "id": "test-request-id",
+            "impid": "test-imp-id",
+            "price": 0.5,
+            "adm": "<blink>Remember this: https://www.google.com/search?q=blink+tag ?</blink>",
+            "crid": "1234567890",
+            "exp": 30,
+            "w": 728,
+            "h": 250
+          },
+          "type": "banner"
+        }
+      ]
+    }
+  ]
+}
diff --git a/adapters/consumable/usersync.go b/adapters/consumable/usersync.go
index 68a93c80..0e0938c7 100644
--- a/adapters/consumable/usersync.go
+++ b/adapters/consumable/usersync.go
@@ -7,7 +7,7 @@ import (
 	"github.com/prebid/prebid-server/usersync"
 )
 
-var VENDOR_ID uint16 = 65535 // TODO: Insert consumable value when one is assigned
+var VENDOR_ID uint16 = 591
 
 func NewConsumableSyncer(temp *template.Template) usersync.Usersyncer {
 
diff --git a/adapters/eplanning/eplanning.go b/adapters/eplanning/eplanning.go
index 49acd30e..5fb9ccf2 100644
--- a/adapters/eplanning/eplanning.go
+++ b/adapters/eplanning/eplanning.go
@@ -23,6 +23,7 @@ const nullSize = "1x1"
 const defaultPageURL = "FILE"
 const sec = "ROS"
 const dfpClientID = "1"
+const requestTargetInventory = "1"
 
 var cleanNameSteps = []cleanNameStep{
 	{regexp.MustCompile(`_|\.|-|\/`), ""},
@@ -117,24 +118,61 @@ func (adapter *EPlanningAdapter) MakeRequests(request *openrtb.BidRequest, reqIn
 		pageDomain = defaultPageURL
 	}
 
-	uri := adapter.URI + fmt.Sprintf("/%s/%s/%s/%s?r=pbs&ncb=1&ur=%s&e=%s", clientID, dfpClientID, pageDomain, sec, url.QueryEscape(pageURL), strings.Join(spacesStrings, "+"))
+	var requestTarget string
+	if request.App != nil && request.App.Bundle != "" {
+		requestTarget = request.App.Bundle
+	} else {
+		requestTarget = pageDomain
+	}
+
+	uriObj, err := url.Parse(adapter.URI)
+	if err != nil {
+		errors = append(errors, err)
+		return nil, errors
+	}
+
+	uriObj.Path = uriObj.Path + fmt.Sprintf("/%s/%s/%s/%s", clientID, dfpClientID, requestTarget, sec)
+	query := url.Values{}
+	query.Set("r", "pbs")
+	query.Set("ncb", "1")
+	if request.App == nil {
+		query.Set("ur", pageURL)
+	}
+	query.Set("e", strings.Join(spacesStrings, "+"))
 
 	if request.User != nil && request.User.BuyerUID != "" {
-		uri = uri + fmt.Sprintf("&uid=%s", request.User.BuyerUID)
+		query.Set("uid", request.User.BuyerUID)
 	}
 
 	if ip != "" {
-		uri = uri + fmt.Sprintf("&ip=%s", ip)
+		query.Set("ip", ip)
 	}
 
 	var body []byte
 	if adapter.testing {
 		body = []byte("{}")
 	} else {
-		uri = uri + fmt.Sprintf("&rnd=%d", rand.Int())
+		t := strconv.Itoa(rand.Int())
+		query.Set("rnd", t)
 		body = nil
 	}
 
+	if request.App != nil {
+		if request.App.Name != "" {
+			query.Set("appn", request.App.Name)
+		}
+		if request.App.ID != "" {
+			query.Set("appid", request.App.ID)
+		}
+		if request.Device != nil && request.Device.IFA != "" {
+			query.Set("ifa", request.Device.IFA)
+		}
+		query.Set("app", requestTargetInventory)
+	}
+
+	uriObj.RawQuery = query.Encode()
+	uri := uriObj.String()
+
 	requestData := adapters.RequestData{
 		Method:  "GET",
 		Uri:     uri,
diff --git a/adapters/mobilefuse/mobilefuse.go b/adapters/mobilefuse/mobilefuse.go
new file mode 100644
index 00000000..3351f10d
--- /dev/null
+++ b/adapters/mobilefuse/mobilefuse.go
@@ -0,0 +1,193 @@
+package mobilefuse
+
+import (
+	"encoding/json"
+	"fmt"
+	"github.com/golang/glog"
+	"github.com/mxmCherry/openrtb"
+	"github.com/prebid/prebid-server/adapters"
+	"github.com/prebid/prebid-server/errortypes"
+	"github.com/prebid/prebid-server/macros"
+	"github.com/prebid/prebid-server/openrtb_ext"
+	"net/http"
+	"strconv"
+	"text/template"
+)
+
+type MobileFuseAdapter struct {
+	EndpointTemplate template.Template
+}
+
+func NewMobileFuseBidder(endpointTemplate string) adapters.Bidder {
+	parsedTemplate, err := template.New("endpointTemplate").Parse(endpointTemplate)
+
+	if err != nil {
+		glog.Fatal("Unable parse endpoint template: " + err.Error())
+		return nil
+	}
+
+	return &MobileFuseAdapter{EndpointTemplate: *parsedTemplate}
+}
+
+func (adapter *MobileFuseAdapter) MakeRequests(request *openrtb.BidRequest, reqInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) {
+	var adapterRequests []*adapters.RequestData
+
+	adapterRequest, errs := adapter.makeRequest(request)
+
+	if errs == nil {
+		adapterRequests = append(adapterRequests, adapterRequest)
+	}
+
+	return adapterRequests, errs
+}
+
+func (adapter *MobileFuseAdapter) MakeBids(incomingRequest *openrtb.BidRequest, outgoingRequest *adapters.RequestData, response *adapters.ResponseData) (*adapters.BidderResponse, []error) {
+	if response.StatusCode == http.StatusNoContent {
+		return nil, nil
+	}
+
+	if response.StatusCode == http.StatusBadRequest {
+		return nil, []error{&errortypes.BadInput{
+			Message: fmt.Sprintf("Unexpected status code: %d.", response.StatusCode),
+		}}
+	}
+
+	if response.StatusCode != http.StatusOK {
+		return nil, []error{&errortypes.BadServerResponse{
+			Message: fmt.Sprintf("Unexpected status code: %d.", response.StatusCode),
+		}}
+	}
+
+	var incomingBidResponse openrtb.BidResponse
+
+	if err := json.Unmarshal(response.Body, &incomingBidResponse); err != nil {
+		return nil, []error{err}
+	}
+
+	outgoingBidResponse := adapters.NewBidderResponseWithBidsCapacity(1)
+
+	for _, seatbid := range incomingBidResponse.SeatBid {
+		for i := range seatbid.Bid {
+			outgoingBidResponse.Bids = append(outgoingBidResponse.Bids, &adapters.TypedBid{
+				Bid:     &seatbid.Bid[i],
+				BidType: adapter.getBidType(seatbid.Bid[i].ImpID, incomingRequest.Imp),
+			})
+		}
+	}
+
+	return outgoingBidResponse, nil
+}
+
+func (adapter *MobileFuseAdapter) makeRequest(bidRequest *openrtb.BidRequest) (*adapters.RequestData, []error) {
+	var errs []error
+
+	mobileFuseExtension, errs := adapter.getFirstMobileFuseExtension(bidRequest)
+
+	if errs != nil {
+		return nil, errs
+	}
+
+	endpoint, err := adapter.getEndpoint(mobileFuseExtension)
+
+	if err != nil {
+		return nil, append(errs, err)
+	}
+
+	validImps := adapter.getValidImps(bidRequest, mobileFuseExtension)
+
+	if len(validImps) == 0 {
+		err := fmt.Errorf("No valid imps")
+		errs = append(errs, err)
+		return nil, errs
+	}
+
+	mobileFuseBidRequest := *bidRequest
+	mobileFuseBidRequest.Imp = validImps
+	body, err := json.Marshal(mobileFuseBidRequest)
+
+	if err != nil {
+		return nil, append(errs, err)
+	}
+
+	headers := http.Header{}
+	headers.Add("Content-Type", "application/json;charset=utf-8")
+	headers.Add("Accept", "application/json")
+
+	return &adapters.RequestData{
+		Method:  "POST",
+		Uri:     endpoint,
+		Body:    body,
+		Headers: headers,
+	}, errs
+}
+
+func (adapter *MobileFuseAdapter) getFirstMobileFuseExtension(request *openrtb.BidRequest) (*openrtb_ext.ExtImpMobileFuse, []error) {
+	var mobileFuseImpExtension openrtb_ext.ExtImpMobileFuse
+	var errs []error
+
+	for _, imp := range request.Imp {
+		var bidder_imp_extension adapters.ExtImpBidder
+
+		err := json.Unmarshal(imp.Ext, &bidder_imp_extension)
+
+		if err != nil {
+			errs = append(errs, err)
+			continue
+		}
+
+		err = json.Unmarshal(bidder_imp_extension.Bidder, &mobileFuseImpExtension)
+
+		if err != nil {
+			errs = append(errs, err)
+			continue
+		}
+
+		break
+	}
+
+	return &mobileFuseImpExtension, errs
+}
+
+func (adapter *MobileFuseAdapter) getEndpoint(ext *openrtb_ext.ExtImpMobileFuse) (string, error) {
+	publisher_id := strconv.Itoa(ext.PublisherId)
+
+	url, errs := macros.ResolveMacros(adapter.EndpointTemplate, macros.EndpointTemplateParams{PublisherID: publisher_id})
+
+	if errs != nil {
+		return "", errs
+	}
+
+	if ext.TagidSrc == "ext" {
+		url += "&tagid_src=ext"
+	}
+
+	return url, nil
+}
+
+func (adapter *MobileFuseAdapter) getValidImps(bidRequest *openrtb.BidRequest, ext *openrtb_ext.ExtImpMobileFuse) []openrtb.Imp {
+	var validImps []openrtb.Imp
+
+	for _, imp := range bidRequest.Imp {
+		if imp.Banner != nil || imp.Video != nil {
+			if imp.Banner != nil && imp.Video != nil {
+				imp.Video = nil
+			}
+
+			imp.TagID = strconv.Itoa(ext.PlacementId)
+			imp.Ext = nil
+			validImps = append(validImps, imp)
+
+			break
+		}
+	}
+
+	return validImps
+}
+
+func (adapter *MobileFuseAdapter) getBidType(imp_id string, imps []openrtb.Imp) openrtb_ext.BidType {
+	if imps[0].Video != nil {
+		return openrtb_ext.BidTypeVideo
+	}
+
+	return openrtb_ext.BidTypeBanner
+}
diff --git a/adapters/visx/visx.go b/adapters/visx/visx.go
index 14fd2b68..418fafd4 100644
--- a/adapters/visx/visx.go
+++ b/adapters/visx/visx.go
@@ -14,11 +14,38 @@ type VisxAdapter struct {
 	endpoint string
 }
 
+type visxBid struct {
+	ImpID   string   `json:"impid"`
+	Price   float64  `json:"price"`
+	UID     int      `json:"auid"`
+	CrID    string   `json:"crid,omitempty"`
+	AdM     string   `json:"adm,omitempty"`
+	ADomain []string `json:"adomain,omitempty"`
+	DealID  string   `json:"dealid,omitempty"`
+	W       uint64   `json:"w,omitempty"`
+	H       uint64   `json:"h,omitempty"`
+}
+
+type visxSeatBid struct {
+	Bid  []visxBid `json:"bid"`
+	Seat string    `json:"seat,omitempty"`
+}
+
+type visxResponse struct {
+	SeatBid []visxSeatBid `json:"seatbid,omitempty"`
+}
+
 // MakeRequests makes the HTTP requests which should be made to fetch bids.
 func (a *VisxAdapter) MakeRequests(request *openrtb.BidRequest, reqInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) {
 	var errors = make([]error, 0)
 
-	reqJSON, err := json.Marshal(request)
+	// copy the request, because we are going to mutate it
+	requestCopy := *request
+	if len(requestCopy.Cur) == 0 {
+		requestCopy.Cur = []string{"USD"}
+	}
+
+	reqJSON, err := json.Marshal(requestCopy)
 	if err != nil {
 		errors = append(errors, err)
 		return nil, errors
@@ -53,7 +80,7 @@ func (a *VisxAdapter) MakeBids(internalRequest *openrtb.BidRequest, externalRequ
 		}}
 	}
 
-	var bidResp openrtb.BidResponse
+	var bidResp visxResponse
 	if err := json.Unmarshal(response.Body, &bidResp); err != nil {
 		return nil, []error{err}
 	}
@@ -62,8 +89,19 @@ func (a *VisxAdapter) MakeBids(internalRequest *openrtb.BidRequest, externalRequ
 
 	for _, sb := range bidResp.SeatBid {
 		for i := range sb.Bid {
+			bid := openrtb.Bid{}
+			bid.ID = internalRequest.ID
+			bid.CrID = sb.Bid[i].CrID
+			bid.ImpID = sb.Bid[i].ImpID
+			bid.Price = sb.Bid[i].Price
+			bid.AdM = sb.Bid[i].AdM
+			bid.W = sb.Bid[i].W
+			bid.H = sb.Bid[i].H
+			bid.ADomain = sb.Bid[i].ADomain
+			bid.DealID = sb.Bid[i].DealID
+
 			bidResponse.Bids = append(bidResponse.Bids, &adapters.TypedBid{
-				Bid:     &sb.Bid[i],
+				Bid:     &bid,
 				BidType: "banner",
 			})
 		}
diff --git a/config/config.go b/config/config.go
index 4c1d6983..5c66f4cd 100755
--- a/config/config.go
+++ b/config/config.go
@@ -142,6 +142,7 @@ type GDPR struct {
 	Timeouts                GDPRTimeouts `mapstructure:"timeouts_ms"`
 	NonStandardPublishers   []string     `mapstructure:"non_standard_publishers,flow"`
 	NonStandardPublisherMap map[string]int
+	AMPException            bool `mapstructure:"amp_exception"`
 }
 
 func (cfg *GDPR) validate(errs configErrors) configErrors {
@@ -213,6 +214,7 @@ func (cfg *HostCookie) TTLDuration() time.Duration {
 const (
 	dummyHost        string = "dummyhost.com"
 	dummyPublisherID string = "12"
+	dummyAccountID   string = "some_account"
 	dummyGDPR        string = "0"
 	dummyGDPRConsent string = "someGDPRConsentString"
 	dummyCCPA        string = "1NYN"
@@ -263,7 +265,7 @@ func validateAdapterEndpoint(endpoint string, adapterName string, errs configErr
 		return append(errs, fmt.Errorf("Invalid endpoint template: %s for adapter: %s. %v", endpoint, adapterName, err))
 	}
 	// Resolve macros (if any) in the endpoint URL
-	resolvedEndpoint, err := macros.ResolveMacros(*endpointTemplate, macros.EndpointTemplateParams{Host: dummyHost, PublisherID: dummyPublisherID})
+	resolvedEndpoint, err := macros.ResolveMacros(*endpointTemplate, macros.EndpointTemplateParams{Host: dummyHost, PublisherID: dummyPublisherID, AccountID: dummyAccountID})
 	if err != nil {
 		return append(errs, fmt.Errorf("Unable to resolve endpoint: %s for adapter: %s. %v", endpoint, adapterName, err))
 	}
@@ -692,6 +694,7 @@ func SetupViper(v *viper.Viper, filename string) {
 	v.SetDefault("adapters.33across.partner_id", "")
 	v.SetDefault("adapters.adform.endpoint", "http://adx.adform.net/adx")
 	v.SetDefault("adapters.adgeneration.endpoint", "https://d.socdm.com/adsv/v1")
+	v.SetDefault("adapters.adhese.endpoint", "https://ads-{{.AccountID}}.adhese.com/json")
 	v.SetDefault("adapters.adkernel.endpoint", "http://{{.Host}}/hb?zone={{.ZoneID}}")
 	v.SetDefault("adapters.adkerneladn.endpoint", "http://{{.Host}}/rtbpub?account={{.PublisherID}}")
 	v.SetDefault("adapters.admixer.endpoint", "http://inv-nets.admixer.net/pbs.aspx")
@@ -728,6 +731,7 @@ func SetupViper(v *viper.Viper, filename string) {
 	v.SetDefault("adapters.lunamedia.endpoint", "http://api.lunamedia.io/xp/get?pubid={{.PublisherID}}")
 	v.SetDefault("adapters.marsmedia.endpoint", "https://bid306.rtbsrv.com/bidder/?bid=f3xtet")
 	v.SetDefault("adapters.mgid.endpoint", "https://prebid.mgid.com/prebid/")
+	v.SetDefault("adapters.mobilefuse.endpoint", "http://mfx-us-east.mobilefuse.com/openrtb?pub_id={{.PublisherID}}")
 	v.SetDefault("adapters.nanointeractive.endpoint", "https://ad.audiencemanager.de/hbs")
 	v.SetDefault("adapters.ninthdecimal.endpoint", "http://rtb.ninthdecimal.com/xp/get?pubid={{.PublisherID}}")
 	v.SetDefault("adapters.openx.endpoint", "http://rtb.openx.net/prebid")
@@ -768,6 +772,7 @@ func SetupViper(v *viper.Viper, filename string) {
 	v.SetDefault("gdpr.timeouts_ms.init_vendorlist_fetches", 0)
 	v.SetDefault("gdpr.timeouts_ms.active_vendorlist_fetch", 0)
 	v.SetDefault("gdpr.non_standard_publishers", []string{""})
+	v.SetDefault("gdpr.amp_exception", false)
 	v.SetDefault("ccpa.enforce", false)
 	v.SetDefault("currency_converter.fetch_url", "https://cdn.jsdelivr.net/gh/prebid/currency-file@1/latest.json")
 	v.SetDefault("currency_converter.fetch_interval_seconds", 1800) // fetch currency rates every 30 minutes
diff --git a/endpoints/openrtb2/amp_auction.go b/endpoints/openrtb2/amp_auction.go
index 97ac8d0c..586481dd 100644
--- a/endpoints/openrtb2/amp_auction.go
+++ b/endpoints/openrtb2/amp_auction.go
@@ -71,6 +71,7 @@ func NewAmpEndpoint(
 		defRequest,
 		defReqJSON,
 		bidderMap,
+		nil,
 		nil}).AmpAuction), nil
 
 }
diff --git a/endpoints/openrtb2/auction.go b/endpoints/openrtb2/auction.go
index 4594a4d5..bcb13724 100644
--- a/endpoints/openrtb2/auction.go
+++ b/endpoints/openrtb2/auction.go
@@ -9,6 +9,7 @@ import (
 	"io/ioutil"
 	"net/http"
 	"net/url"
+	"regexp"
 	"strconv"
 	"time"
 
@@ -57,6 +58,7 @@ func NewEndpoint(ex exchange.Exchange, validator openrtb_ext.BidderParamValidato
 		defRequest,
 		defReqJSON,
 		bidderMap,
+		nil,
 		nil}).Auction), nil
 }
 
@@ -74,6 +76,7 @@ type endpointDeps struct {
 	defReqJSON       []byte
 	bidderMap        map[string]openrtb_ext.BidderName
 	cache            prebid_cache_client.Client
+	debugLogRegexp   *regexp.Regexp
 }
 
 func (deps *endpointDeps) Auction(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
diff --git a/endpoints/openrtb2/video_auction.go b/endpoints/openrtb2/video_auction.go
index cf764bc9..020a5196 100644
--- a/endpoints/openrtb2/video_auction.go
+++ b/endpoints/openrtb2/video_auction.go
@@ -9,12 +9,13 @@ import (
 	"io/ioutil"
 	"net/http"
 	"net/url"
+	"regexp"
 	"strconv"
 	"strings"
 	"time"
 
 	"github.com/buger/jsonparser"
-	"github.com/evanphx/json-patch"
+	jsonpatch "github.com/evanphx/json-patch"
 	"github.com/gofrs/uuid"
 	"github.com/prebid/prebid-server/errortypes"
 
@@ -40,7 +41,9 @@ func NewVideoEndpoint(ex exchange.Exchange, validator openrtb_ext.BidderParamVal
 	}
 	defRequest := defReqJSON != nil && len(defReqJSON) > 0
 
-	return httprouter.Handle((&endpointDeps{ex, validator, requestsById, videoFetcher, categories, cfg, met, pbsAnalytics, disabledBidders, defRequest, defReqJSON, bidderMap, cache}).VideoAuctionEndpoint), nil
+	videoEndpointRegexp := regexp.MustCompile(`[<>]`)
+
+	return httprouter.Handle((&endpointDeps{ex, validator, requestsById, videoFetcher, categories, cfg, met, pbsAnalytics, disabledBidders, defRequest, defReqJSON, bidderMap, cache, videoEndpointRegexp}).VideoAuctionEndpoint), nil
 }
 
 /*
@@ -89,29 +92,17 @@ func (deps *endpointDeps) VideoAuctionEndpoint(w http.ResponseWriter, r *http.Re
 		cacheTTL = int64(deps.cfg.CacheURL.DefaultTTLs.Video)
 	}
 	debugLog := exchange.DebugLog{
-		EnableDebug: strings.EqualFold(debugQuery, "true"),
-		CacheType:   prebid_cache_client.TypeXML,
-		TTL:         cacheTTL,
+		Enabled:   strings.EqualFold(debugQuery, "true"),
+		CacheType: prebid_cache_client.TypeXML,
+		TTL:       cacheTTL,
+		Regexp:    deps.debugLogRegexp,
 	}
 
 	defer func() {
 		if len(debugLog.CacheKey) > 0 && vo.VideoResponse == nil {
-			debugLog.Data = fmt.Sprintf("<!--\n%s\n\nNo response created\n-->", debugLog.Data)
-			data, err := json.Marshal(debugLog.Data)
-			if err == nil {
-				toCache := []prebid_cache_client.Cacheable{
-					{
-						Type:       debugLog.CacheType,
-						Data:       data,
-						TTLSeconds: debugLog.TTL,
-						Key:        "log_" + debugLog.CacheKey,
-					},
-				}
-				if deps.cache != nil {
-					ctx, cancel := context.WithDeadline(context.Background(), start.Add(time.Duration(100)*time.Millisecond))
-					defer cancel()
-					deps.cache.PutJson(ctx, toCache)
-				}
+			err := putDebugLogError(deps.cache, &debugLog, start)
+			if err != nil {
+				vo.Errors = append(vo.Errors, err)
 			}
 		}
 		deps.metricsEngine.RecordRequest(labels)
@@ -130,12 +121,12 @@ func (deps *endpointDeps) VideoAuctionEndpoint(w http.ResponseWriter, r *http.Re
 	}
 
 	resolvedRequest := requestJson
-	if debugLog.EnableDebug {
-		debugLog.Data = fmt.Sprintf("Request:\n%s", string(requestJson))
+	if debugLog.Enabled {
+		debugLog.Data.Request = string(requestJson)
 		if headerBytes, err := json.Marshal(r.Header); err == nil {
-			debugLog.Data = fmt.Sprintf("%s\n\nHeaders:\n%s", debugLog.Data, string(headerBytes))
+			debugLog.Data.Headers = string(headerBytes)
 		} else {
-			debugLog.Data = fmt.Sprintf("%s\n\nUnable to marshal headers data\n", debugLog.Data)
+			debugLog.Data.Headers = fmt.Sprintf("Unable to marshal headers data: %s", err.Error())
 		}
 	}
 
@@ -182,7 +173,7 @@ func (deps *endpointDeps) VideoAuctionEndpoint(w http.ResponseWriter, r *http.Re
 	//create full open rtb req from full video request
 	mergeData(videoBidReq, bidReq)
 	// If debug query param is set, force the response to enable test flag
-	if debugLog.EnableDebug {
+	if debugLog.Enabled {
 		bidReq.Test = 1
 	}
 
@@ -281,6 +272,34 @@ func (deps *endpointDeps) VideoAuctionEndpoint(w http.ResponseWriter, r *http.Re
 
 }
 
+func putDebugLogError(cache prebid_cache_client.Client, debugLog *exchange.DebugLog, start time.Time) error {
+	debugLog.Data.Response = "No response created"
+
+	debugLog.BuildCacheString()
+
+	data, err := json.Marshal(debugLog.CacheString)
+	if err != nil {
+		return err
+	}
+
+	toCache := []prebid_cache_client.Cacheable{
+		{
+			Type:       debugLog.CacheType,
+			Data:       data,
+			TTLSeconds: debugLog.TTL,
+			Key:        "log_" + debugLog.CacheKey,
+		},
+	}
+
+	if cache != nil {
+		ctx, cancel := context.WithDeadline(context.Background(), start.Add(time.Duration(100)*time.Millisecond))
+		defer cancel()
+		cache.PutJson(ctx, toCache)
+	}
+
+	return nil
+}
+
 func cleanupVideoBidRequest(videoReq *openrtb_ext.BidRequestVideo, podErrors []PodError) *openrtb_ext.BidRequestVideo {
 	for i := len(podErrors) - 1; i >= 0; i-- {
 		videoReq.PodConfig.Pods = append(videoReq.PodConfig.Pods[:podErrors[i].PodIndex], videoReq.PodConfig.Pods[podErrors[i].PodIndex+1:]...)
@@ -289,7 +308,7 @@ func cleanupVideoBidRequest(videoReq *openrtb_ext.BidRequestVideo, podErrors []P
 }
 
 func handleError(labels *pbsmetrics.Labels, w http.ResponseWriter, errL []error, vo *analytics.VideoObject, debugLog *exchange.DebugLog) {
-	if debugLog != nil && debugLog.EnableDebug {
+	if debugLog != nil && debugLog.Enabled {
 		if rawUUID, err := uuid.NewV4(); err == nil {
 			debugLog.CacheKey = rawUUID.String()
 		}
diff --git a/exchange/adapter_map.go b/exchange/adapter_map.go
index 9110b20f..b91f01a7 100755
--- a/exchange/adapter_map.go
+++ b/exchange/adapter_map.go
@@ -9,6 +9,7 @@ import (
 	ttx "github.com/prebid/prebid-server/adapters/33across"
 	"github.com/prebid/prebid-server/adapters/adform"
 	"github.com/prebid/prebid-server/adapters/adgeneration"
+	"github.com/prebid/prebid-server/adapters/adhese"
 	"github.com/prebid/prebid-server/adapters/adkernel"
 	"github.com/prebid/prebid-server/adapters/adkernelAdn"
 	"github.com/prebid/prebid-server/adapters/admixer"
@@ -44,6 +45,7 @@ import (
 	"github.com/prebid/prebid-server/adapters/lunamedia"
 	"github.com/prebid/prebid-server/adapters/marsmedia"
 	"github.com/prebid/prebid-server/adapters/mgid"
+	"github.com/prebid/prebid-server/adapters/mobilefuse"
 	"github.com/prebid/prebid-server/adapters/nanointeractive"
 	"github.com/prebid/prebid-server/adapters/ninthdecimal"
 	"github.com/prebid/prebid-server/adapters/openx"
@@ -86,6 +88,7 @@ func newAdapterMap(client *http.Client, cfg *config.Configuration, infos adapter
 		openrtb_ext.Bidder33Across:     ttx.New33AcrossBidder(cfg.Adapters[string(openrtb_ext.Bidder33Across)].Endpoint),
 		openrtb_ext.BidderAdform:       adform.NewAdformBidder(client, cfg.Adapters[string(openrtb_ext.BidderAdform)].Endpoint),
 		openrtb_ext.BidderAdgeneration: adgeneration.NewAdgenerationAdapter(cfg.Adapters[string(openrtb_ext.BidderAdgeneration)].Endpoint),
+		openrtb_ext.BidderAdhese:       adhese.NewAdheseBidder(cfg.Adapters[string(openrtb_ext.BidderAdhese)].Endpoint),
 		openrtb_ext.BidderAdkernel:     adkernel.NewAdkernelAdapter(cfg.Adapters[strings.ToLower(string(openrtb_ext.BidderAdkernel))].Endpoint),
 		openrtb_ext.BidderAdkernelAdn:  adkernelAdn.NewAdkernelAdnAdapter(cfg.Adapters[strings.ToLower(string(openrtb_ext.BidderAdkernelAdn))].Endpoint),
 		openrtb_ext.BidderAdmixer:      admixer.NewAdmixerBidder(cfg.Adapters[strings.ToLower(string(openrtb_ext.BidderAdmixer))].Endpoint),
@@ -121,6 +124,7 @@ func newAdapterMap(client *http.Client, cfg *config.Configuration, infos adapter
 		openrtb_ext.BidderLunaMedia:       lunamedia.NewLunaMediaBidder(cfg.Adapters[string(openrtb_ext.BidderLunaMedia)].Endpoint),
 		openrtb_ext.BidderMarsmedia:       marsmedia.NewMarsmediaBidder(cfg.Adapters[string(openrtb_ext.BidderMarsmedia)].Endpoint),
 		openrtb_ext.BidderMgid:            mgid.NewMgidBidder(cfg.Adapters[string(openrtb_ext.BidderMgid)].Endpoint),
+		openrtb_ext.BidderMobileFuse:      mobilefuse.NewMobileFuseBidder(cfg.Adapters[string(openrtb_ext.BidderMobileFuse)].Endpoint),
 		openrtb_ext.BidderNanoInteractive: nanointeractive.NewNanoIneractiveBidder(cfg.Adapters[string(openrtb_ext.BidderNanoInteractive)].Endpoint),
 		openrtb_ext.BidderNinthDecimal:    ninthdecimal.NewNinthDecimalBidder(cfg.Adapters[string(openrtb_ext.BidderNinthDecimal)].Endpoint),
 		openrtb_ext.BidderOrbidder:        orbidder.NewOrbidderBidder(cfg.Adapters[string(openrtb_ext.BidderOrbidder)].Endpoint),
diff --git a/exchange/auction.go b/exchange/auction.go
index 9909b78d..dcb73708 100644
--- a/exchange/auction.go
+++ b/exchange/auction.go
@@ -3,8 +3,10 @@ package exchange
 import (
 	"context"
 	"encoding/json"
+	"encoding/xml"
 	"errors"
 	"fmt"
+	"regexp"
 	"strings"
 
 	uuid "github.com/gofrs/uuid"
@@ -15,6 +17,36 @@ import (
 	"github.com/prebid/prebid-server/prebid_cache_client"
 )
 
+type DebugLog struct {
+	Enabled     bool
+	CacheType   prebid_cache_client.PayloadType
+	Data        DebugData
+	TTL         int64
+	CacheKey    string
+	CacheString string
+	Regexp      *regexp.Regexp
+}
+
+type DebugData struct {
+	Request  string
+	Headers  string
+	Response string
+}
+
+func (d *DebugLog) BuildCacheString() {
+	if d.Regexp != nil {
+		d.Data.Request = fmt.Sprintf(d.Regexp.ReplaceAllString(d.Data.Request, ""))
+		d.Data.Headers = fmt.Sprintf(d.Regexp.ReplaceAllString(d.Data.Headers, ""))
+		d.Data.Response = fmt.Sprintf(d.Regexp.ReplaceAllString(d.Data.Response, ""))
+	}
+
+	d.Data.Request = fmt.Sprintf("<Request>%s</Request>", d.Data.Request)
+	d.Data.Headers = fmt.Sprintf("<Headers>%s</Headers>", d.Data.Headers)
+	d.Data.Response = fmt.Sprintf("<Response>%s</Response>", d.Data.Response)
+
+	d.CacheString = fmt.Sprintf("%s<Log>%s%s%s</Log>", xml.Header, d.Data.Request, d.Data.Headers, d.Data.Response)
+}
+
 func newAuction(seatBids map[openrtb_ext.BidderName]*pbsOrtbSeatBid, numImps int) *auction {
 	winningBids := make(map[string]*pbsOrtbBid, numImps)
 	winningBidsByBidder := make(map[string]map[openrtb_ext.BidderName]*pbsOrtbBid, numImps)
@@ -147,9 +179,10 @@ func (a *auction) doCache(ctx context.Context, cache prebid_cache_client.Client,
 		}
 	}
 
-	if debugLog != nil && debugLog.EnableDebug {
+	if debugLog != nil && debugLog.Enabled {
+		debugLog.BuildCacheString()
 		debugLog.CacheKey = hbCacheID
-		if jsonBytes, err := json.Marshal(debugLog.Data); err == nil {
+		if jsonBytes, err := json.Marshal(debugLog.CacheString); err == nil {
 			toCache = append(toCache, prebid_cache_client.Cacheable{
 				Type:       debugLog.CacheType,
 				Data:       jsonBytes,
diff --git a/exchange/exchange.go b/exchange/exchange.go
index 48903dd9..6d51b87d 100644
--- a/exchange/exchange.go
+++ b/exchange/exchange.go
@@ -27,14 +27,6 @@ import (
 	"github.com/prebid/prebid-server/prebid_cache_client"
 )
 
-type DebugLog struct {
-	EnableDebug bool
-	CacheType   prebid_cache_client.PayloadType
-	Data        string
-	TTL         int64
-	CacheKey    string
-}
-
 // Exchange runs Auctions. Implementations must be threadsafe, and will be shared across many goroutines.
 type Exchange interface {
 	// HoldAuction executes an OpenRTB v2.5 Auction.
@@ -174,25 +166,33 @@ func (e *exchange) HoldAuction(ctx context.Context, bidRequest *openrtb.BidReque
 
 		if targData != nil {
 			auc.setRoundedPrices(targData.priceGranularity)
-			if debugLog != nil && debugLog.EnableDebug {
-				bidResponseExt = e.makeExtBidResponse(adapterBids, adapterExtra, bidRequest, resolvedRequest, errs)
-				if bidRespExtBytes, err := json.Marshal(bidResponseExt); err == nil {
-					debugLog.Data = fmt.Sprintf("<!--\n%s\n\nResponse:\n%s\n-->", debugLog.Data, string(bidRespExtBytes))
-				} else {
-					errs = append(errs, errors.New("Unable to marshal response ext for debugging"))
-				}
-			}
 
 			if requestExt.Prebid.SupportDeals {
 				dealErrs := applyDealSupport(bidRequest, auc, bidCategory)
 				errs = append(errs, dealErrs...)
 			}
 
+			if debugLog != nil && debugLog.Enabled {
+				bidResponseExt = e.makeExtBidResponse(adapterBids, adapterExtra, bidRequest, resolvedRequest, errs)
+				if bidRespExtBytes, err := json.Marshal(bidResponseExt); err == nil {
+					debugLog.Data.Response = string(bidRespExtBytes)
+				} else {
+					debugLog.Data.Response = "Unable to marshal response ext for debugging"
+					errs = append(errs, errors.New(debugLog.Data.Response))
+				}
+			}
+
 			cacheErrs := auc.doCache(ctx, e.cache, targData, bidRequest, 60, &e.defaultTTLs, bidCategory, debugLog)
 			if len(cacheErrs) > 0 {
 				errs = append(errs, cacheErrs...)
 			}
 			targData.setTargeting(auc, bidRequest.App != nil, bidCategory)
+
+			// Ensure caching errors are added if the bid response ext has already been created
+			if bidResponseExt != nil && len(cacheErrs) > 0 {
+				bidderCacheErrs := errsToBidderErrors(cacheErrs)
+				bidResponseExt.Errors[openrtb_ext.PrebidExtKey] = append(bidResponseExt.Errors[openrtb_ext.PrebidExtKey], bidderCacheErrs...)
+			}
 		}
 
 	}
diff --git a/exchange/utils.go b/exchange/utils.go
index f09b1151..d961089c 100644
--- a/exchange/utils.go
+++ b/exchange/utils.go
@@ -43,6 +43,7 @@ func cleanOpenRTBRequests(ctx context.Context,
 
 	gdpr := extractGDPR(orig, usersyncIfAmbiguous)
 	consent := extractConsent(orig)
+	ampGDPRException := (labels.RType == pbsmetrics.ReqTypeAMP) && gDPR.AMPException()
 
 	privacyEnforcement := privacy.Enforcement{
 		COPPA: orig.Regs != nil && orig.Regs.COPPA == 1,
@@ -65,7 +66,7 @@ func cleanOpenRTBRequests(ctx context.Context,
 			privacyEnforcement.GDPR = false
 		}
 
-		privacyEnforcement.Apply(bidReq)
+		privacyEnforcement.Apply(bidReq, ampGDPRException)
 	}
 
 	return
diff --git a/gdpr/gdpr.go b/gdpr/gdpr.go
index 4e36e22f..9390d942 100644
--- a/gdpr/gdpr.go
+++ b/gdpr/gdpr.go
@@ -24,6 +24,9 @@ type Permissions interface {
 	//
 	// If the consent string was nonsensical, the returned error will be an ErrorMalformedConsent.
 	PersonalInfoAllowed(ctx context.Context, bidder openrtb_ext.BidderName, PublisherID string, consent string) (bool, error)
+
+	// Exposes the AMP execption flag
+	AMPException() bool
 }
 
 const (
diff --git a/gdpr/impl.go b/gdpr/impl.go
index 615c3a09..8743d7f2 100644
--- a/gdpr/impl.go
+++ b/gdpr/impl.go
@@ -58,6 +58,10 @@ func (p *permissionsImpl) PersonalInfoAllowed(ctx context.Context, bidder openrt
 	return false, nil
 }
 
+func (p *permissionsImpl) AMPException() bool {
+	return p.cfg.AMPException
+}
+
 func (p *permissionsImpl) allowSync(ctx context.Context, vendorID uint16, consent string) (bool, error) {
 	// If we're not given a consent string, respect the preferences in the app config.
 	if consent == "" {
@@ -145,3 +149,7 @@ func (a AlwaysAllow) BidderSyncAllowed(ctx context.Context, bidder openrtb_ext.B
 func (a AlwaysAllow) PersonalInfoAllowed(ctx context.Context, bidder openrtb_ext.BidderName, PublisherID string, consent string) (bool, error) {
 	return true, nil
 }
+
+func (a AlwaysAllow) AMPException() bool {
+	return false
+}
diff --git a/macros/macros.go b/macros/macros.go
index 9f9cfad2..a9f77ea9 100644
--- a/macros/macros.go
+++ b/macros/macros.go
@@ -11,6 +11,7 @@ type EndpointTemplateParams struct {
 	PublisherID string
 	ZoneID      string
 	SourceId    string
+	AccountID   string
 }
 
 // UserSyncTemplateParams specifies params for an user sync URL template
diff --git a/openrtb_ext/bidders.go b/openrtb_ext/bidders.go
index a0a8c6a8..f2f8e7c6 100755
--- a/openrtb_ext/bidders.go
+++ b/openrtb_ext/bidders.go
@@ -27,6 +27,7 @@ const (
 	Bidder33Across         BidderName = "33across"
 	BidderAdform           BidderName = "adform"
 	BidderAdgeneration     BidderName = "adgeneration"
+	BidderAdhese           BidderName = "adhese"
 	BidderAdkernel         BidderName = "adkernel"
 	BidderAdkernelAdn      BidderName = "adkernelAdn"
 	BidderAdpone           BidderName = "adpone"
@@ -62,6 +63,7 @@ const (
 	BidderLunaMedia        BidderName = "lunamedia"
 	BidderMarsmedia        BidderName = "marsmedia"
 	BidderMgid             BidderName = "mgid"
+	BidderMobileFuse       BidderName = "mobilefuse"
 	BidderNanoInteractive  BidderName = "nanointeractive"
 	BidderNinthDecimal     BidderName = "ninthdecimal"
 	BidderOpenx            BidderName = "openx"
@@ -100,6 +102,7 @@ var BidderMap = map[string]BidderName{
 	"33across":          Bidder33Across,
 	"adform":            BidderAdform,
 	"adgeneration":      BidderAdgeneration,
+	"adhese":            BidderAdhese,
 	"adkernel":          BidderAdkernel,
 	"adkernelAdn":       BidderAdkernelAdn,
 	"admixer":           BidderAdmixer,
@@ -135,6 +138,7 @@ var BidderMap = map[string]BidderName{
 	"lunamedia":         BidderLunaMedia,
 	"marsmedia":         BidderMarsmedia,
 	"mgid":              BidderMgid,
+	"mobilefuse":        BidderMobileFuse,
 	"nanointeractive":   BidderNanoInteractive,
 	"ninthdecimal":      BidderNinthDecimal,
 	"openx":             BidderOpenx,
diff --git a/openrtb_ext/imp_adhese.go b/openrtb_ext/imp_adhese.go
new file mode 100644
index 00000000..1c822018
--- /dev/null
+++ b/openrtb_ext/imp_adhese.go
@@ -0,0 +1,12 @@
+package openrtb_ext
+
+import (
+	"encoding/json"
+)
+
+type ExtImpAdhese struct {
+	Account  string          `json:"account"`
+	Location string          `json:"location"`
+	Format   string          `json:"format"`
+	Keywords json.RawMessage `json:"targets,omitempty"`
+}
diff --git a/openrtb_ext/imp_mobilefuse.go b/openrtb_ext/imp_mobilefuse.go
new file mode 100644
index 00000000..ea53c591
--- /dev/null
+++ b/openrtb_ext/imp_mobilefuse.go
@@ -0,0 +1,8 @@
+package openrtb_ext
+
+// ExtImpMobileFuse defines the contract for bidrequest.imp[i].ext.mobilefuse
+type ExtImpMobileFuse struct {
+	PlacementId int    `json:"placement_id"`
+	PublisherId int    `json:"pub_id"`
+	TagidSrc    string `json:"tagid_src"`
+}
diff --git a/privacy/ccpa/policy.go b/privacy/ccpa/policy.go
index 8b50e111..64579f2a 100644
--- a/privacy/ccpa/policy.go
+++ b/privacy/ccpa/policy.go
@@ -5,7 +5,6 @@ import (
 	"errors"
 	"fmt"
 
-	"github.com/buger/jsonparser"
 	"github.com/mxmCherry/openrtb"
 	"github.com/prebid/prebid-server/openrtb_ext"
 )
@@ -41,12 +40,22 @@ func (p Policy) Write(req *openrtb.BidRequest) error {
 	}
 
 	if req.Regs.Ext == nil {
-		req.Regs.Ext = json.RawMessage(`{"us_privacy":"` + p.Value + `"}`)
-		return nil
+		ext, err := json.Marshal(openrtb_ext.ExtRegs{USPrivacy: p.Value})
+		if err == nil {
+			req.Regs.Ext = ext
+		}
+		return err
 	}
 
-	var err error
-	req.Regs.Ext, err = jsonparser.Set(req.Regs.Ext, []byte(`"`+p.Value+`"`), "us_privacy")
+	var extMap map[string]interface{}
+	err := json.Unmarshal(req.Regs.Ext, &extMap)
+	if err == nil {
+		extMap["us_privacy"] = p.Value
+		ext, err := json.Marshal(extMap)
+		if err == nil {
+			req.Regs.Ext = ext
+		}
+	}
 	return err
 }
 
diff --git a/privacy/enforcement.go b/privacy/enforcement.go
index 0230ca6b..96d03ef4 100644
--- a/privacy/enforcement.go
+++ b/privacy/enforcement.go
@@ -17,14 +17,14 @@ func (e Enforcement) Any() bool {
 }
 
 // Apply cleans personally identifiable information from an OpenRTB bid request.
-func (e Enforcement) Apply(bidRequest *openrtb.BidRequest) {
-	e.apply(bidRequest, NewScrubber())
+func (e Enforcement) Apply(bidRequest *openrtb.BidRequest, ampGDPRException bool) {
+	e.apply(bidRequest, ampGDPRException, NewScrubber())
 }
 
-func (e Enforcement) apply(bidRequest *openrtb.BidRequest, scrubber Scrubber) {
+func (e Enforcement) apply(bidRequest *openrtb.BidRequest, ampGDPRException bool, scrubber Scrubber) {
 	if bidRequest != nil && e.Any() {
 		bidRequest.Device = scrubber.ScrubDevice(bidRequest.Device, e.getIPv6ScrubStrategy(), e.getGeoScrubStrategy())
-		bidRequest.User = scrubber.ScrubUser(bidRequest.User, e.getDemographicScrubStrategy(), e.getGeoScrubStrategy())
+		bidRequest.User = scrubber.ScrubUser(bidRequest.User, e.getUserScrubStrategy(ampGDPRException), e.getGeoScrubStrategy())
 	}
 }
 
@@ -52,10 +52,13 @@ func (e Enforcement) getGeoScrubStrategy() ScrubStrategyGeo {
 	return ScrubStrategyGeoNone
 }
 
-func (e Enforcement) getDemographicScrubStrategy() ScrubStrategyDemographic {
+func (e Enforcement) getUserScrubStrategy(ampGDPRException bool) ScrubStrategyUser {
 	if e.COPPA {
-		return ScrubStrategyDemographicAgeAndGender
+		return ScrubStrategyUserIDAndDemographic
 	}
 
-	return ScrubStrategyDemographicNone
+	if e.GDPR && ampGDPRException {
+		return ScrubStrategyUserNone
+	}
+	return ScrubStrategyUserID
 }
diff --git a/privacy/gdpr/policy.go b/privacy/gdpr/policy.go
index a4e1bc6a..4733e1ed 100644
--- a/privacy/gdpr/policy.go
+++ b/privacy/gdpr/policy.go
@@ -2,8 +2,8 @@ package gdpr
 
 import (
 	"encoding/json"
+	"github.com/prebid/prebid-server/openrtb_ext"
 
-	"github.com/buger/jsonparser"
 	"github.com/mxmCherry/openrtb"
 	"github.com/prebid/go-gdpr/vendorconsent"
 )
@@ -25,12 +25,22 @@ func (p Policy) Write(req *openrtb.BidRequest) error {
 	}
 
 	if req.User.Ext == nil {
-		req.User.Ext = json.RawMessage(`{"consent":"` + p.Consent + `"}`)
-		return nil
+		ext, err := json.Marshal(openrtb_ext.ExtUser{Consent: p.Consent})
+		if err == nil {
+			req.User.Ext = ext
+		}
+		return err
 	}
 
-	var err error
-	req.User.Ext, err = jsonparser.Set(req.User.Ext, []byte(`"`+p.Consent+`"`), "consent")
+	var extMap map[string]interface{}
+	err := json.Unmarshal(req.User.Ext, &extMap)
+	if err == nil {
+		extMap["consent"] = p.Consent
+		ext, err := json.Marshal(extMap)
+		if err == nil {
+			req.User.Ext = ext
+		}
+	}
 	return err
 }
 
diff --git a/privacy/scrubber.go b/privacy/scrubber.go
index 45b79c20..ec313e91 100644
--- a/privacy/scrubber.go
+++ b/privacy/scrubber.go
@@ -34,21 +34,24 @@ const (
 	ScrubStrategyGeoReducedPrecision
 )
 
-// ScrubStrategyDemographic defines the approach to non-location demographic data.
-type ScrubStrategyDemographic int
+// ScrubStrategyUser defines the approach to scrub PII from user data.
+type ScrubStrategyUser int
 
 const (
-	// ScrubStrategyDemographicNone does not remove non-location demographic data.
-	ScrubStrategyDemographicNone ScrubStrategyDemographic = iota
+	// ScrubStrategyUserNone does not remove non-location data.
+	ScrubStrategyUserNone ScrubStrategyUser = iota
 
-	// ScrubStrategyDemographicAgeAndGender removes age and gender data.
-	ScrubStrategyDemographicAgeAndGender
+	// ScrubStrategyUserIDAndDemographic removes the user's buyer id, exchange id year of birth, and gender.
+	ScrubStrategyUserIDAndDemographic
+
+	// ScrubStrategyUserID removes the user's buyer id.
+	ScrubStrategyUserID
 )
 
 // Scrubber removes PII from parts of an OpenRTB request.
 type Scrubber interface {
 	ScrubDevice(device *openrtb.Device, ipv6 ScrubStrategyIPV6, geo ScrubStrategyGeo) *openrtb.Device
-	ScrubUser(user *openrtb.User, demographic ScrubStrategyDemographic, geo ScrubStrategyGeo) *openrtb.User
+	ScrubUser(user *openrtb.User, strategy ScrubStrategyUser, geo ScrubStrategyGeo) *openrtb.User
 }
 
 type scrubber struct{}
@@ -90,19 +93,22 @@ func (scrubber) ScrubDevice(device *openrtb.Device, ipv6 ScrubStrategyIPV6, geo
 	return &deviceCopy
 }
 
-func (scrubber) ScrubUser(user *openrtb.User, demographic ScrubStrategyDemographic, geo ScrubStrategyGeo) *openrtb.User {
+func (scrubber) ScrubUser(user *openrtb.User, strategy ScrubStrategyUser, geo ScrubStrategyGeo) *openrtb.User {
 	if user == nil {
 		return nil
 	}
 
 	userCopy := *user
-	userCopy.BuyerUID = ""
-	userCopy.ID = ""
 
-	switch demographic {
-	case ScrubStrategyDemographicAgeAndGender:
+	switch strategy {
+	case ScrubStrategyUserIDAndDemographic:
+		userCopy.BuyerUID = ""
+		userCopy.ID = ""
 		userCopy.Yob = 0
 		userCopy.Gender = ""
+	case ScrubStrategyUserID:
+		userCopy.BuyerUID = ""
+		userCopy.ID = ""
 	}
 
 	switch geo {
diff --git a/router/router.go b/router/router.go
index 68627f93..045c86ef 100644
--- a/router/router.go
+++ b/router/router.go
@@ -6,13 +6,14 @@ import (
 	"database/sql"
 	"encoding/json"
 	"fmt"
-	"github.com/prebid/prebid-server/pbsmetrics"
 	"io/ioutil"
 	"net/http"
 	"path/filepath"
 	"strings"
 	"time"
 
+	"github.com/prebid/prebid-server/pbsmetrics"
+
 	"github.com/prebid/prebid-server/adapters"
 	"github.com/prebid/prebid-server/adapters/adform"
 	"github.com/prebid/prebid-server/adapters/appnexus"
diff --git a/static/bidder-info/adhese.yaml b/static/bidder-info/adhese.yaml
new file mode 100644
index 00000000..742d7834
--- /dev/null
+++ b/static/bidder-info/adhese.yaml
@@ -0,0 +1,11 @@
+maintainer:
+  email: info@adhese.com
+capabilities:
+  app:
+    mediaTypes:
+      - banner
+      - video
+  site:
+    mediaTypes:
+      - banner
+      - video
\ No newline at end of file
diff --git a/static/bidder-info/mobilefuse.yaml b/static/bidder-info/mobilefuse.yaml
new file mode 100644
index 00000000..178e407d
--- /dev/null
+++ b/static/bidder-info/mobilefuse.yaml
@@ -0,0 +1,7 @@
+maintainer:
+  email: prebid@mobilefuse.com
+capabilities:
+  app:
+    mediaTypes:
+      - banner
+      - video
diff --git a/static/bidder-params/adhese.json b/static/bidder-params/adhese.json
new file mode 100644
index 00000000..a1bd608b
--- /dev/null
+++ b/static/bidder-params/adhese.json
@@ -0,0 +1,25 @@
+{
+	"$schema": "http://json-schema.org/draft-04/schema#",
+	"title": "Adhese Adapter Parameters",
+	"description": "Validation for parameters handled by the Adhese adapter",
+	"type": "object",
+	"properties": {
+		"account": {
+			"type": "string",
+			"description": "Your Adhese account name. If unknown, please contact your sales rep"
+		},
+		"location": {
+			"type": "string",
+			"description": "The location you want to refer to for a specific section or page, as defined in your Adhese inventory"
+		},
+		"format": {
+			"type": "string",
+			"description": "The format you accept for this unit, as defined in your Adhese inventory"
+		},
+		"targets": {
+			"type": "object",
+			"description": "Target params, as defined in your Adhese setup."
+		  }
+	},
+	"required": ["account", "location", "format"]
+}
diff --git a/static/bidder-params/mobilefuse.json b/static/bidder-params/mobilefuse.json
new file mode 100644
index 00000000..15f17148
--- /dev/null
+++ b/static/bidder-params/mobilefuse.json
@@ -0,0 +1,24 @@
+{
+    "$schema": "http://json-schema.org/draft-04/schema#",
+    "title": "MobileFuse Adapter Params",
+    "description": "A schema which validates params accepted by the MobileFuse adapter",
+    "type": "object",
+    "properties": {
+        "placement_id": {
+            "type": "integer",
+            "description": "An ID which identifies this specific inventory placement"
+        },
+        "pub_id": {
+            "type": "integer",
+            "description": "An ID which identifies the publisher selling the inventory."
+        },
+        "tagid_src": {
+            "type": "string",
+            "description": "ext if passing publisher's ids, empty if passing MobileFuse IDs in placement_id field. Defaults to empty"
+        }
+    },
+    "required": [
+        "placement_id",
+        "pub_id"
+    ]
+}
\ No newline at end of file


Test patch:
diff --git a/adapters/adhese/adhese_test.go b/adapters/adhese/adhese_test.go
new file mode 100644
index 00000000..0ce8fb59
--- /dev/null
+++ b/adapters/adhese/adhese_test.go
@@ -0,0 +1,11 @@
+package adhese
+
+import (
+	"testing"
+
+	"github.com/prebid/prebid-server/adapters/adapterstest"
+)
+
+func TestJsonSamples(t *testing.T) {
+	adapterstest.RunJSONBidderTest(t, "adhesetest", NewAdheseBidder("https://ads-{{.AccountID}}.adhese.com/json"))
+}
diff --git a/adapters/adhese/adhesetest/exemplary/banner-internal.json b/adapters/adhese/adhesetest/exemplary/banner-internal.json
new file mode 100644
index 00000000..3a31d0cc
--- /dev/null
+++ b/adapters/adhese/adhesetest/exemplary/banner-internal.json
@@ -0,0 +1,103 @@
+{
+  "mockBidRequest": {
+    "id": "test-req",
+    "user": {
+      "ext": {
+        "consent" : "dummy"
+      }
+    },
+    "imp": [
+      {
+        "id": "test-req",
+        "banner": {
+          "format": [
+            {
+              "w": 300,
+              "h": 250
+            }
+          ]
+        },
+        "ext": {
+          "bidder": {
+            "account": "demo",
+            "location": "_adhese_prebid_demo_",
+            "format": "leaderboard",
+            "targets": 
+            {
+              "ci": ["gent", "brussels"],
+              "ag": ["55"],
+              "tl": ["all"]
+            }
+          }
+        }
+      }
+    ],
+    "site": {
+      "id": "test",
+      "publisher": {
+        "id": "123"
+      }
+    }
+  },
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "https://ads-demo.adhese.com/json/sl_adhese_prebid_demo_-leaderboard/ag55/cigent;brussels/tlall/xtdummy"
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": [{
+          "origin": "JERLICIA",
+          "originInstance": "",
+          "ext": "js",
+          "slotName": "_main_page_-leaderboard",
+          "adType": "leaderboard",
+          "orderId": "888",
+          "id": "60613369",
+          "width": "728",
+          "height": "90",
+          "body": "<div style='background-color:red; height:250px; width:300px'></div>",
+          "tracker": "https://hosts-demo.adhese.com/rtb_gateway/handlers/client/track/?id=a2f39296-6dd0-4b3c-be85-7baa22e7ff4a",
+          "impressionCounter": "https://hosts-demo.adhese.com/rtb_gateway/handlers/client/track/?id=a2f39296-6dd0-4b3c-be85-7baa22e7ff4a",
+          "extension": {
+            "prebid": {
+              "cpm": {
+                "amount": "1",
+                "currency": "USD"
+              }
+            }
+          }
+        }]
+      }
+    }
+  ],
+  "expectedBidResponses": [
+    {
+      "currency": "USD",
+        "bids": [
+          {
+            "bid": {
+              "adm": "<div style='background-color:red; height:250px; width:300px'></div><img src='https://hosts-demo.adhese.com/rtb_gateway/handlers/client/track/?id=a2f39296-6dd0-4b3c-be85-7baa22e7ff4a' style='height:1px; width:1px; margin: -1px -1px; display:none;'/>",
+              "w": 728,
+              "h": 90,
+              "id": "",
+              "impid": "",
+              "price": 1.000000,
+              "dealid": "888",
+              "crid": "60613369",
+              "ext": {
+                    "adFormat": "",
+                    "adType": "leaderboard",
+                    "adspaceId": "",
+                    "libId": "",
+                    "orderProperty": "",
+                    "priority": "",
+                    "viewableImpressionCounter": ""
+                  }
+            },
+            "type": "banner"
+          }
+        ]
+    }
+  ]
+}
\ No newline at end of file
diff --git a/adapters/adhese/adhesetest/exemplary/banner-market.json b/adapters/adhese/adhesetest/exemplary/banner-market.json
new file mode 100644
index 00000000..340e6d87
--- /dev/null
+++ b/adapters/adhese/adhesetest/exemplary/banner-market.json
@@ -0,0 +1,100 @@
+{
+  "mockBidRequest": {
+    "id": "test-req",
+    "imp": [
+      {
+        "id": "test-req",
+        "banner": {
+          "format": [
+            {
+              "w": 300,
+              "h": 250
+            }
+          ]
+        },
+        "ext": {
+          "bidder": {
+            "account": "market",
+            "location": "_adhese_prebid_demo_",
+            "format": "leaderboard",
+            "targets": 
+            {
+              "ci": ["gent", "brussels"],
+              "ag": ["55"],
+              "tl": ["all"]
+            }
+          }
+        }
+      }
+    ],
+    "site": {
+      "id": "test",
+      "publisher": {
+        "id": "123"
+      },
+      "page": "dummy"
+    }
+  },
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "https://ads-market.adhese.com/json/sl_adhese_prebid_demo_-leaderboard/ag55/cigent;brussels/tlall/xfdummy"
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": [{
+          "origin": "APPNEXUS",
+          "originInstance": "",
+          "ext": "js",
+          "slotName": "_main_page_-leaderboard",
+          "adType": "leaderboard",
+          "originData": {
+            "seatbid": [
+              {
+                "bid": [
+                  {
+                    "crid": "60613369",
+                    "dealid": null
+                  }
+                ],
+                "seat": "958"
+              }
+            ]
+          },
+          "width": "728",
+          "height": "90",
+          "body": "<div style='background-color:red; height:250px; width:300px'></div>",
+          "tracker": "https://hosts-demo.adhese.com/rtb_gateway/handlers/client/track/?id=a2f39296-6dd0-4b3c-be85-7baa22e7ff4a",
+          "impressionCounter": "https://hosts-demo.adhese.com/rtb_gateway/handlers/client/track/?id=a2f39296-6dd0-4b3c-be85-7baa22e7ff4a",
+          "extension": {
+            "prebid": {
+              "cpm": {
+                "amount": "1",
+                "currency": "USD"
+              }
+            }
+          }
+        }]
+      }
+    }
+  ],
+  "expectedBidResponses": [
+    {
+      "currency": "USD",
+        "bids": [
+          {
+            "bid": {
+              "adm": "<div style='background-color:red; height:250px; width:300px'></div>",
+              "w": 728,
+              "h": 90,
+              "id": "",
+              "impid": "",
+              "crid": "60613369",
+              "price": 1.000000
+            },
+            "type": "banner"
+          }
+        ]
+    }
+  ]
+}
\ No newline at end of file
diff --git a/adapters/adhese/adhesetest/exemplary/banner-video-internal.json b/adapters/adhese/adhesetest/exemplary/banner-video-internal.json
new file mode 100644
index 00000000..bd9203c2
--- /dev/null
+++ b/adapters/adhese/adhesetest/exemplary/banner-video-internal.json
@@ -0,0 +1,103 @@
+{
+  "mockBidRequest": {
+    "id": "test-req",
+    "user": {
+      "ext": {
+        "consent" : "dummy"
+      }
+    },
+    "imp": [
+      {
+        "id": "test-req",
+        "banner": {
+          "format": [
+            {
+              "w": 300,
+              "h": 250
+            }
+          ]
+        },
+        "ext": {
+          "bidder": {
+            "account": "demo",
+            "location": "_adhese_prebid_demo_",
+            "format": "leaderboard",
+            "targets": 
+            {
+              "ci": ["gent", "brussels"],
+              "ag": ["55"],
+              "tl": ["all"]
+            }
+          }
+        }
+      }
+    ],
+    "site": {
+      "id": "test",
+      "publisher": {
+        "id": "123"
+      }
+    }
+  },
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "https://ads-demo.adhese.com/json/sl_adhese_prebid_demo_-leaderboard/ag55/cigent;brussels/tlall/xtdummy"
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": [{
+          "origin": "JERLICIA",
+          "originInstance": "",
+          "ext": "js",
+          "slotName": "_main_page_-leaderboard",
+          "adType": "leaderboard",
+          "orderId": "888",
+          "id": "60613369",
+          "width": "728",
+          "height": "90",
+          "body": "<?xml version=\"1.0\" encoding=\"UTF-8\"?><abc>dummy</abc>",
+          "tracker": "https://hosts-demo.adhese.com/rtb_gateway/handlers/client/track/?id=a2f39296-6dd0-4b3c-be85-7baa22e7ff4a",
+          "impressionCounter": "https://hosts-demo.adhese.com/rtb_gateway/handlers/client/track/?id=a2f39296-6dd0-4b3c-be85-7baa22e7ff4a",
+          "extension": {
+            "prebid": {
+              "cpm": {
+                "amount": "1",
+                "currency": "USD"
+              }
+            }
+          }
+        }]
+      }
+    }
+  ],
+  "expectedBidResponses": [
+    {
+      "currency": "USD",
+        "bids": [
+          {
+            "bid": {
+              "adm": "<?xml version=\"1.0\" encoding=\"UTF-8\"?><abc>dummy</abc>",
+              "w": 728,
+              "h": 90,
+              "id": "",
+              "impid": "",
+              "price": 1.000000,
+              "dealid": "888",
+              "crid": "60613369",
+              "ext": {
+                    "adFormat": "",
+                    "adType": "leaderboard",
+                    "adspaceId": "",
+                    "libId": "",
+                    "orderProperty": "",
+                    "priority": "",
+                    "viewableImpressionCounter": ""
+                  }
+            },
+            "type": "video"
+          }
+        ]
+    }
+  ]
+}
\ No newline at end of file
diff --git a/adapters/adhese/adhesetest/exemplary/video.json b/adapters/adhese/adhesetest/exemplary/video.json
new file mode 100644
index 00000000..134e3add
--- /dev/null
+++ b/adapters/adhese/adhesetest/exemplary/video.json
@@ -0,0 +1,80 @@
+{
+    "mockBidRequest": {
+        "imp": [{
+            "ext": {
+                "bidder": {
+                    "account": "demo",
+                    "location": "_adhese_prebid_demo_",
+                    "format": "leaderboard",
+                    "targets":
+                    {
+                      "ci": ["gent", "brussels"],
+                      "ag": ["55"],
+                      "tl": ["all"]
+                    }
+                }
+            }
+        }]
+     },
+  
+    "httpCalls": [
+      {
+        "expectedRequest": {
+          "uri": "https://ads-demo.adhese.com/json/sl_adhese_prebid_demo_-leaderboard/ag55/cigent;brussels/tlall"
+        },
+        "mockResponse": {
+          "status": 200,
+          "body": [{
+            "origin": "RUBICON",
+            "ext": "js",
+            "slotName": "_main_page_-leaderboard",
+            "adType": "leaderboard",
+            "originData": {
+              "seatbid": [
+                {
+                  "bid": [
+                    {
+                      "crid": "60613369",
+                      "dealid": null
+                    }
+                  ],
+                  "seat": "958"
+                }
+              ]
+            },
+            "width": "640",
+            "height": "350",
+            "body": "<?xml version=\"1.0\" encoding=\"UTF-8\"?><VAST xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" version=\"2.0\" xsi:noNamespaceSchemaLocation=\"vast.xsd\"></VAST>",
+            "extension": {
+                "prebid": {
+                    "cpm": {
+                        "amount": "2.1", 
+                        "currency": "USD"
+                        }
+                    }
+                }
+            }]
+        }
+      }
+    ],
+  
+    "expectedBidResponses": [
+      {
+        "currency": "USD",
+          "bids": [
+            {
+              "bid": {
+                "adm": "<?xml version=\"1.0\" encoding=\"UTF-8\"?><VAST xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" version=\"2.0\" xsi:noNamespaceSchemaLocation=\"vast.xsd\"></VAST>",
+                "w": 640,
+                "h": 350,
+                "id": "",
+                "impid": "",
+                "crid": "60613369",
+                "price": 2.1
+              },
+              "type": "video"
+            }
+          ]
+      }
+  ]
+  }
\ No newline at end of file
diff --git a/adapters/adhese/adhesetest/supplemental/req-invalid-empty-imp-ext.json b/adapters/adhese/adhesetest/supplemental/req-invalid-empty-imp-ext.json
new file mode 100644
index 00000000..e98823b2
--- /dev/null
+++ b/adapters/adhese/adhesetest/supplemental/req-invalid-empty-imp-ext.json
@@ -0,0 +1,38 @@
+{
+  "mockBidRequest": {
+    "id": "test-req",
+    "user": {
+      "ext": {
+        "consent" : "dummy"
+      }
+    },
+    "imp": [
+      {
+        "id": "test-req",
+        "banner": {
+          "format": [
+            {
+              "w": 300,
+              "h": 250
+            }
+          ]
+        },
+        "ext": {}
+      }
+    ],
+    "site": {
+      "id": "test",
+      "publisher": {
+        "id": "123"
+      }
+    }
+  },
+  "httpCalls": [],
+  "expectedBidResponses": [],
+  "expectedMakeRequestsErrors": [
+    {
+      "value": "Request could not be parsed as ExtImpAdhese due to: unexpected end of JSON input",
+      "comparison": "literal"
+    }
+  ]
+}
\ No newline at end of file
diff --git a/adapters/adhese/adhesetest/supplemental/req-invalid-empty-imp.json b/adapters/adhese/adhesetest/supplemental/req-invalid-empty-imp.json
new file mode 100644
index 00000000..a4fc8230
--- /dev/null
+++ b/adapters/adhese/adhesetest/supplemental/req-invalid-empty-imp.json
@@ -0,0 +1,25 @@
+{
+    "mockBidRequest": {
+      "id": "test-req",
+      "user": {
+        "ext": {
+          "consent" : "dummy"
+        }
+      },
+      "imp": [],
+      "site": {
+        "id": "test",
+        "publisher": {
+          "id": "123"
+        }
+      }
+    },
+    "httpCalls": [],
+    "expectedBidResponses": [],
+    "expectedMakeRequestsErrors": [
+      {
+        "value": "Imp is empty",
+        "comparison": "literal"
+      }
+    ]
+  }
\ No newline at end of file
diff --git a/adapters/adhese/adhesetest/supplemental/req-invalid-no-imp-ext.json b/adapters/adhese/adhesetest/supplemental/req-invalid-no-imp-ext.json
new file mode 100644
index 00000000..bcb89171
--- /dev/null
+++ b/adapters/adhese/adhesetest/supplemental/req-invalid-no-imp-ext.json
@@ -0,0 +1,37 @@
+{
+    "mockBidRequest": {
+      "id": "test-req",
+      "user": {
+        "ext": {
+          "consent" : "dummy"
+        }
+      },
+      "imp": [
+        {
+          "id": "test-req",
+          "banner": {
+            "format": [
+              {
+                "w": 300,
+                "h": 250
+              }
+            ]
+          }
+        }
+      ],
+      "site": {
+        "id": "test",
+        "publisher": {
+          "id": "123"
+        }
+      }
+    },
+    "httpCalls": [],
+    "expectedBidResponses": [],
+    "expectedMakeRequestsErrors": [
+      {
+        "value": "Request could not be parsed as ExtImpBidder due to: unexpected end of JSON input",
+        "comparison": "literal"
+      }
+    ]
+  }
\ No newline at end of file
diff --git a/adapters/adhese/adhesetest/supplemental/res-invalid-height.json b/adapters/adhese/adhesetest/supplemental/res-invalid-height.json
new file mode 100644
index 00000000..1e11df2f
--- /dev/null
+++ b/adapters/adhese/adhesetest/supplemental/res-invalid-height.json
@@ -0,0 +1,91 @@
+{
+  "mockBidRequest": {
+    "id": "test-req",
+    "imp": [
+      {
+        "id": "test-req",
+        "banner": {
+          "format": [
+            {
+              "w": 300,
+              "h": 250
+            }
+          ]
+        },
+        "ext": {
+          "bidder": {
+            "account": "market",
+            "location": "_adhese_prebid_demo_",
+            "format": "leaderboard",
+            "targets": 
+            {
+              "ci": ["gent", "brussels"],
+              "ag": ["55"],
+              "tl": ["all"]
+            }
+          }
+        }
+      }
+    ],
+    "site": {
+      "id": "test",
+      "publisher": {
+        "id": "123"
+      },
+      "page": "dummy"
+    }
+  },
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "https://ads-market.adhese.com/json/sl_adhese_prebid_demo_-leaderboard/ag55/cigent;brussels/tlall/xfdummy"
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": [{
+          "origin": "APPNEXUS",
+          "originInstance": "",
+          "ext": "js",
+          "slotName": "_main_page_-leaderboard",
+          "adType": "leaderboard",
+          "originData": {
+            "seatbid": [
+              {
+                "bid": [
+                  {
+                    "crid": "60613369",
+                    "dealid": null
+                  }
+                ],
+                "seat": "958"
+              }
+            ]
+          },
+          "width": "728",
+          "height": 90,
+          "body": "<div style='background-color:red; height:250px; width:300px'></div>",
+          "tracker": "https://hosts-demo.adhese.com/rtb_gateway/handlers/client/track/?id=a2f39296-6dd0-4b3c-be85-7baa22e7ff4a",
+          "impressionCounter": "https://hosts-demo.adhese.com/rtb_gateway/handlers/client/track/?id=a2f39296-6dd0-4b3c-be85-7baa22e7ff4a",
+          "extension": {
+            "prebid": {
+              "cpm": {
+                "amount": "10",
+                "currency": "USD"
+              }
+            }
+          }
+        }]
+      }
+    }
+  ],
+  "expectedBidResponses": [],
+    "expectedMakeBidsErrors": [
+      {
+        "value": "json: cannot unmarshal number into Go struct field AdheseBid.height of type string",
+        "comparison": "literal"
+      }, {
+        "value": "Response (.*\n.*)+ could not be parsed as generic Adhese bid",
+        "comparison": "regex"
+      }
+    ]
+}
\ No newline at end of file
diff --git a/adapters/adhese/adhesetest/supplemental/res-invalid-no-body.json b/adapters/adhese/adhesetest/supplemental/res-invalid-no-body.json
new file mode 100644
index 00000000..f9704764
--- /dev/null
+++ b/adapters/adhese/adhesetest/supplemental/res-invalid-no-body.json
@@ -0,0 +1,62 @@
+{
+    "mockBidRequest": {
+      "id": "test-req",
+      "user": {
+        "ext": {
+          "consent" : "dummy"
+        }
+      },
+      "imp": [
+        {
+          "id": "test-req",
+          "banner": {
+            "format": [
+              {
+                "w": 300,
+                "h": 250
+              }
+            ]
+          },
+          "ext": {
+            "bidder": {
+              "account": "demo",
+              "location": "_adhese_prebid_demo_",
+              "format": "leaderboard",
+              "targets": 
+              {
+                "ci": ["gent", "brussels"],
+                "ag": ["55"],
+                "tl": ["all"]
+              }
+            }
+          }
+        }
+      ],
+      "site": {
+        "id": "test",
+        "publisher": {
+          "id": "123"
+        }
+      }
+    },
+    "httpCalls": [
+      {
+        "expectedRequest": {
+          "uri": "https://ads-demo.adhese.com/json/sl_adhese_prebid_demo_-leaderboard/ag55/cigent;brussels/tlall/xtdummy"
+        },
+        "mockResponse": {
+          "status": 200
+        }
+      }
+    ],
+    "expectedBidResponses": [],
+    "expectedMakeBidsErrors": [
+      {
+        "value": "unexpected end of JSON input",
+        "comparison": "literal"
+      },{
+        "value": "Response  could not be parsed as generic Adhese bid.",
+        "comparison": "literal"
+      }
+    ]
+  }
\ No newline at end of file
diff --git a/adapters/adhese/adhesetest/supplemental/res-invalid-no-origin.json b/adapters/adhese/adhesetest/supplemental/res-invalid-no-origin.json
new file mode 100644
index 00000000..636e3074
--- /dev/null
+++ b/adapters/adhese/adhesetest/supplemental/res-invalid-no-origin.json
@@ -0,0 +1,80 @@
+{
+    "mockBidRequest": {
+      "id": "test-req",
+      "user": {
+        "ext": {
+          "consent" : "dummy"
+        }
+      },
+      "imp": [
+        {
+          "id": "test-req",
+          "banner": {
+            "format": [
+              {
+                "w": 300,
+                "h": 250
+              }
+            ]
+          },
+          "ext": {
+            "bidder": {
+              "account": "demo",
+              "location": "_adhese_prebid_demo_",
+              "format": "leaderboard",
+              "targets": 
+              {
+                "ci": ["gent", "brussels"],
+                "ag": ["55"],
+                "tl": ["all"]
+              }
+            }
+          }
+        }
+      ],
+      "site": {
+        "id": "test",
+        "publisher": {
+          "id": "123"
+        }
+      }
+    },
+    "httpCalls": [
+      {
+        "expectedRequest": {
+          "uri": "https://ads-demo.adhese.com/json/sl_adhese_prebid_demo_-leaderboard/ag55/cigent;brussels/tlall/xtdummy"
+        },
+        "mockResponse": {
+          "status": 200,
+          "body": [{
+            "originInstance": "",
+            "ext": "js",
+            "slotName": "_main_page_-leaderboard",
+            "adType": "leaderboard",
+            "orderId": "888",
+            "id": "60613369",
+            "width": "728",
+            "height": "90",
+            "body": "<div style='background-color:red; height:250px; width:300px'></div>",
+            "tracker": "https://hosts-demo.adhese.com/rtb_gateway/handlers/client/track/?id=a2f39296-6dd0-4b3c-be85-7baa22e7ff4a",
+            "impressionCounter": "https://hosts-demo.adhese.com/rtb_gateway/handlers/client/track/?id=a2f39296-6dd0-4b3c-be85-7baa22e7ff4a",
+            "extension": {
+              "prebid": {
+                "cpm": {
+                  "amount": "1",
+                  "currency": "USD"
+                }
+              }
+            }
+          }]
+        }
+      }
+    ],
+    "expectedBidResponses": [],
+    "expectedMakeBidsErrors": [
+      {
+        "value": "Response resulted in an empty seatBid array.",
+        "comparison": "literal"
+      }
+    ]
+  }
\ No newline at end of file
diff --git a/adapters/adhese/adhesetest/supplemental/res-invalid-price.json b/adapters/adhese/adhesetest/supplemental/res-invalid-price.json
new file mode 100644
index 00000000..f23bf0f7
--- /dev/null
+++ b/adapters/adhese/adhesetest/supplemental/res-invalid-price.json
@@ -0,0 +1,91 @@
+{
+  "mockBidRequest": {
+    "id": "test-req",
+    "imp": [
+      {
+        "id": "test-req",
+        "banner": {
+          "format": [
+            {
+              "w": 300,
+              "h": 250
+            }
+          ]
+        },
+        "ext": {
+          "bidder": {
+            "account": "market",
+            "location": "_adhese_prebid_demo_",
+            "format": "leaderboard",
+            "targets": 
+            {
+              "ci": ["gent", "brussels"],
+              "ag": ["55"],
+              "tl": ["all"]
+            }
+          }
+        }
+      }
+    ],
+    "site": {
+      "id": "test",
+      "publisher": {
+        "id": "123"
+      },
+      "page": "dummy"
+    }
+  },
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "https://ads-market.adhese.com/json/sl_adhese_prebid_demo_-leaderboard/ag55/cigent;brussels/tlall/xfdummy"
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": [{
+          "origin": "APPNEXUS",
+          "originInstance": "",
+          "ext": "js",
+          "slotName": "_main_page_-leaderboard",
+          "adType": "leaderboard",
+          "originData": {
+            "seatbid": [
+              {
+                "bid": [
+                  {
+                    "crid": "60613369",
+                    "dealid": null
+                  }
+                ],
+                "seat": "958"
+              }
+            ]
+          },
+          "width": "728",
+          "height": "90",
+          "body": "<div style='background-color:red; height:250px; width:300px'></div>",
+          "tracker": "https://hosts-demo.adhese.com/rtb_gateway/handlers/client/track/?id=a2f39296-6dd0-4b3c-be85-7baa22e7ff4a",
+          "impressionCounter": "https://hosts-demo.adhese.com/rtb_gateway/handlers/client/track/?id=a2f39296-6dd0-4b3c-be85-7baa22e7ff4a",
+          "extension": {
+            "prebid": {
+              "cpm": {
+                "amount": 10,
+                "currency": "USD"
+              }
+            }
+          }
+        }]
+      }
+    }
+  ],
+  "expectedBidResponses": [],
+    "expectedMakeBidsErrors": [
+      {
+        "value": "json: cannot unmarshal number into Go struct field CPMValues.*\\.amount of type string",
+        "comparison": "regex"
+      }, {
+        "value": "Response (.*\n.*)+ could not be parsed as generic Adhese bid",
+        "comparison": "regex"
+      }
+    ]
+}
\ No newline at end of file
diff --git a/adapters/adhese/adhesetest/supplemental/res-invalid-status-not-ok.json b/adapters/adhese/adhesetest/supplemental/res-invalid-status-not-ok.json
new file mode 100644
index 00000000..96f17b15
--- /dev/null
+++ b/adapters/adhese/adhesetest/supplemental/res-invalid-status-not-ok.json
@@ -0,0 +1,81 @@
+{
+    "mockBidRequest": {
+      "id": "test-req",
+      "user": {
+        "ext": {
+          "consent" : "dummy"
+        }
+      },
+      "imp": [
+        {
+          "id": "test-req",
+          "banner": {
+            "format": [
+              {
+                "w": 300,
+                "h": 250
+              }
+            ]
+          },
+          "ext": {
+            "bidder": {
+              "account": "demo",
+              "location": "_adhese_prebid_demo_",
+              "format": "leaderboard",
+              "targets": 
+              {
+                "ci": ["gent", "brussels"],
+                "ag": ["55"],
+                "tl": ["all"]
+              }
+            }
+          }
+        }
+      ],
+      "site": {
+        "id": "test",
+        "publisher": {
+          "id": "123"
+        }
+      }
+    },
+    "httpCalls": [
+      {
+        "expectedRequest": {
+          "uri": "https://ads-demo.adhese.com/json/sl_adhese_prebid_demo_-leaderboard/ag55/cigent;brussels/tlall/xtdummy"
+        },
+        "mockResponse": {
+          "status": 501,
+          "body": [{
+            "origin": "JERLICIA",
+            "originInstance": "",
+            "ext": "js",
+            "slotName": "_main_page_-leaderboard",
+            "adType": "leaderboard",
+            "orderId": "888",
+            "id": "60613369",
+            "width": "728",
+            "height": "90",
+            "body": "<div style='background-color:red; height:250px; width:300px'></div>",
+            "tracker": "https://hosts-demo.adhese.com/rtb_gateway/handlers/client/track/?id=a2f39296-6dd0-4b3c-be85-7baa22e7ff4a",
+            "impressionCounter": "https://hosts-demo.adhese.com/rtb_gateway/handlers/client/track/?id=a2f39296-6dd0-4b3c-be85-7baa22e7ff4a",
+            "extension": {
+              "prebid": {
+                "cpm": {
+                  "amount": "1",
+                  "currency": "USD"
+                }
+              }
+            }
+          }]
+        }
+      }
+    ],
+    "expectedBidResponses": [],
+    "expectedMakeBidsErrors": [
+      {
+        "value": "Unexpected status code: 501.",
+        "comparison": "literal"
+      }
+    ]
+  }
\ No newline at end of file
diff --git a/adapters/adhese/adhesetest/supplemental/res-invalid-width.json b/adapters/adhese/adhesetest/supplemental/res-invalid-width.json
new file mode 100644
index 00000000..77b23d34
--- /dev/null
+++ b/adapters/adhese/adhesetest/supplemental/res-invalid-width.json
@@ -0,0 +1,91 @@
+{
+  "mockBidRequest": {
+    "id": "test-req",
+    "imp": [
+      {
+        "id": "test-req",
+        "banner": {
+          "format": [
+            {
+              "w": 300,
+              "h": 250
+            }
+          ]
+        },
+        "ext": {
+          "bidder": {
+            "account": "market",
+            "location": "_adhese_prebid_demo_",
+            "format": "leaderboard",
+            "targets": 
+            {
+              "ci": ["gent", "brussels"],
+              "ag": ["55"],
+              "tl": ["all"]
+            }
+          }
+        }
+      }
+    ],
+    "site": {
+      "id": "test",
+      "publisher": {
+        "id": "123"
+      },
+      "page": "dummy"
+    }
+  },
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "https://ads-market.adhese.com/json/sl_adhese_prebid_demo_-leaderboard/ag55/cigent;brussels/tlall/xfdummy"
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": [{
+          "origin": "APPNEXUS",
+          "originInstance": "",
+          "ext": "js",
+          "slotName": "_main_page_-leaderboard",
+          "adType": "leaderboard",
+          "originData": {
+            "seatbid": [
+              {
+                "bid": [
+                  {
+                    "crid": "60613369",
+                    "dealid": null
+                  }
+                ],
+                "seat": "958"
+              }
+            ]
+          },
+          "width": 728,
+          "height": "90",
+          "body": "<div style='background-color:red; height:250px; width:300px'></div>",
+          "tracker": "https://hosts-demo.adhese.com/rtb_gateway/handlers/client/track/?id=a2f39296-6dd0-4b3c-be85-7baa22e7ff4a",
+          "impressionCounter": "https://hosts-demo.adhese.com/rtb_gateway/handlers/client/track/?id=a2f39296-6dd0-4b3c-be85-7baa22e7ff4a",
+          "extension": {
+            "prebid": {
+              "cpm": {
+                "amount": "10",
+                "currency": "USD"
+              }
+            }
+          }
+        }]
+      }
+    }
+  ],
+  "expectedBidResponses": [],
+    "expectedMakeBidsErrors": [
+      {
+        "value": "json: cannot unmarshal number into Go struct field AdheseBid.width of type string",
+        "comparison": "literal"
+      }, {
+        "value": "Response (.*\n.*)+ could not be parsed as generic Adhese bid",
+        "comparison": "regex"
+      }
+    ]
+}
\ No newline at end of file
diff --git a/adapters/adhese/adhesetest/supplemental/res-no_bids.json b/adapters/adhese/adhesetest/supplemental/res-no_bids.json
new file mode 100644
index 00000000..c9630049
--- /dev/null
+++ b/adapters/adhese/adhesetest/supplemental/res-no_bids.json
@@ -0,0 +1,54 @@
+{
+  "mockBidRequest": {
+    "id": "test-req",
+    "user": {
+      "ext": {
+        "consent" : "dummy"
+      }
+    },
+    "imp": [
+      {
+        "id": "test-req",
+        "banner": {
+          "format": [
+            {
+              "w": 300,
+              "h": 250
+            }
+          ]
+        },
+        "ext": {
+          "bidder": {
+            "account": "demo",
+            "location": "_adhese_prebid_demo_",
+            "format": "leaderboard",
+            "targets": 
+            {
+              "ci": ["gent", "brussels"],
+              "ag": ["55"],
+              "tl": ["all"]
+            }
+          }
+        }
+      }
+    ],
+    "site": {
+      "id": "test",
+      "publisher": {
+        "id": "123"
+      }
+    }
+  },
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "https://ads-demo.adhese.com/json/sl_adhese_prebid_demo_-leaderboard/ag55/cigent;brussels/tlall/xtdummy"
+      },
+      "mockResponse": {
+        "status": 204,
+        "body": []
+      }
+    }
+  ],
+  "expectedBidResponses": []
+}
\ No newline at end of file
diff --git a/adapters/adhese/adhesetest/supplemental/res-no_impression_counter.json b/adapters/adhese/adhesetest/supplemental/res-no_impression_counter.json
new file mode 100644
index 00000000..99af5671
--- /dev/null
+++ b/adapters/adhese/adhesetest/supplemental/res-no_impression_counter.json
@@ -0,0 +1,103 @@
+{
+  "mockBidRequest": {
+    "id": "test-req",
+    "user": {
+      "ext": {
+        "consent" : "dummy"
+      }
+    },
+    "imp": [
+      {
+        "id": "test-req",
+        "banner": {
+          "format": [
+            {
+              "w": 300,
+              "h": 250
+            }
+          ]
+        },
+        "ext": {
+          "bidder": {
+            "account": "demo",
+            "location": "_adhese_prebid_demo_",
+            "format": "leaderboard",
+            "targets": 
+            {
+              "ci": ["gent", "brussels"],
+              "ag": ["55"],
+              "tl": ["all"]
+            }
+          }
+        }
+      }
+    ],
+    "site": {
+      "id": "test",
+      "publisher": {
+        "id": "123"
+      }
+    }
+  },
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "https://ads-demo.adhese.com/json/sl_adhese_prebid_demo_-leaderboard/ag55/cigent;brussels/tlall/xtdummy"
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": [{
+          "origin": "JERLICIA",
+          "originInstance": "",
+          "ext": "js",
+          "slotName": "_main_page_-leaderboard",
+          "adType": "leaderboard",
+          "orderId": "888",
+          "id": "60613369",
+          "width": "728",
+          "height": "90",
+          "body": "<div style='background-color:red; height:250px; width:300px'></div>",
+          "tracker": "https://hosts-demo.adhese.com/rtb_gateway/handlers/client/track/?id=a2f39296-6dd0-4b3c-be85-7baa22e7ff4a",
+          "impressionCounter": "",
+          "extension": {
+            "prebid": {
+              "cpm": {
+                "amount": "1",
+                "currency": "USD"
+              }
+            }
+          }
+        }]
+      }
+    }
+  ],
+  "expectedBidResponses": [
+    {
+      "currency": "USD",
+        "bids": [
+          {
+            "bid": {
+              "adm": "<div style='background-color:red; height:250px; width:300px'></div>",
+              "w": 728,
+              "h": 90,
+              "id": "",
+              "impid": "",
+              "price": 1.000000,
+              "dealid": "888",
+              "crid": "60613369",
+              "ext": {
+                    "adFormat": "",
+                    "adType": "leaderboard",
+                    "adspaceId": "",
+                    "libId": "",
+                    "orderProperty": "",
+                    "priority": "",
+                    "viewableImpressionCounter": ""
+                  }
+            },
+            "type": "banner"
+          }
+        ]
+    }
+  ]
+}
\ No newline at end of file
diff --git a/adapters/adhese/params_test.go b/adapters/adhese/params_test.go
new file mode 100644
index 00000000..45024749
--- /dev/null
+++ b/adapters/adhese/params_test.go
@@ -0,0 +1,58 @@
+package adhese
+
+import (
+	"encoding/json"
+	"fmt"
+	"testing"
+
+	"github.com/prebid/prebid-server/openrtb_ext"
+)
+
+func TestValidParams(t *testing.T) {
+	validator, err := openrtb_ext.NewBidderParamsValidator("../../static/bidder-params")
+	if err != nil {
+		t.Fatalf("Failed to fetch the json-schemas. %v", err)
+	}
+
+	for _, validParam := range validParams {
+		if err := validator.Validate(openrtb_ext.BidderAdhese, json.RawMessage(validParam)); err != nil {
+			fmt.Println(err)
+			t.Errorf("Schema rejected Adhese params: %s", validParam)
+		}
+	}
+}
+
+func TestInvalidParams(t *testing.T) {
+	validator, err := openrtb_ext.NewBidderParamsValidator("../../static/bidder-params")
+	if err != nil {
+		t.Fatalf("Failed to fetch the json-schemas. %v", err)
+	}
+
+	for _, invalidParam := range invalidParams {
+		if err := validator.Validate(openrtb_ext.BidderAdhese, json.RawMessage(invalidParam)); err == nil {
+			t.Errorf("Schema allowed unexpected params: %s", invalidParam)
+		}
+	}
+}
+
+var validParams = []string{
+	`{"account": "demo", "location": "loc1", "format": "for1"}`,
+	`{"account": "demo", "location": "loc1", "format": "for1", "targets": { "ab": ["test", "test2"]}}`,
+}
+
+var invalidParams = []string{
+	`null`,
+	`nil`,
+	``,
+	`[]`,
+	`true`,
+	`{"location": "loc1", "format": "for1"}`,
+	`{"account": "demo", "format": "for1"}`,
+	`{"account": "demo", "location": "loc1"}`,
+	`{"account": "demo", "location": "loc1", "format": "for1", "targets": null`,
+	`{"account": 5, "location": "loc1", "format": "for1"}`,
+	`{"account": "demo", "location": 5, "format": "for1"}`,
+	`{"account": "demo", "location": "loc1", "format": 5}`,
+	`{"account": "demo", "location": "loc1", "format": "for1", "targets": "test"}`,
+	`{"account": "demo", "location": "loc1", "format": "for1", "targets": 5}`,
+}
diff --git a/adapters/advangelists/usersync_test.go b/adapters/advangelists/usersync_test.go
index b97652c1..2167d49f 100644
--- a/adapters/advangelists/usersync_test.go
+++ b/adapters/advangelists/usersync_test.go
@@ -26,6 +26,6 @@ func TestAdvangelistsSyncer(t *testing.T) {
 	assert.NoError(t, err)
 	assert.Equal(t, "https://nep.advangelists.com/xp/user-sync?acctid={aid}&&redirect=localhost/setuid?bidder=advangelists&gdpr=1&gdpr_consent=BOPVK28OVJoTBABABAENBs-AAAAhuAKAANAAoACwAGgAPAAxAB0AHgAQAAiABOADkA&uid=$UID", syncInfo.URL)
 	assert.Equal(t, "iframe", syncInfo.Type)
-	assert.EqualValues(t, 61, syncer.GDPRVendorID())
+	assert.EqualValues(t, 0, syncer.GDPRVendorID())
 	assert.Equal(t, false, syncInfo.SupportCORS)
 }
diff --git a/adapters/consumable/usersync_test.go b/adapters/consumable/usersync_test.go
index eb97edf2..017cb729 100644
--- a/adapters/consumable/usersync_test.go
+++ b/adapters/consumable/usersync_test.go
@@ -30,6 +30,6 @@ func TestConsumableSyncer(t *testing.T) {
 	assert.NoError(t, err)
 	assert.Equal(t, "//e.serverbid.com/udb/9969/match?gdpr=A&euconsent=B&us_privacy=C&redir=http%3A%2F%2Flocalhost%3A8000%2Fsetuid%3Fbidder%3Dconsumable%26gdpr%3DA%26gdpr_consent%3DB%26uid%3D", u.URL)
 	assert.Equal(t, "redirect", u.Type)
-	assert.Equal(t, uint16(65535), syncer.GDPRVendorID())
+	assert.Equal(t, uint16(591), syncer.GDPRVendorID())
 	assert.Equal(t, false, u.SupportCORS)
 }
diff --git a/adapters/eplanning/eplanningtest/exemplary/simple-banner-2.json b/adapters/eplanning/eplanningtest/exemplary/simple-banner-2.json
index f4c8fe0c..e602877f 100644
--- a/adapters/eplanning/eplanningtest/exemplary/simple-banner-2.json
+++ b/adapters/eplanning/eplanningtest/exemplary/simple-banner-2.json
@@ -28,7 +28,7 @@
   "httpCalls": [
     {
       "expectedRequest": {
-        "uri": "https://ads.us.e-planning.net/hb/1/12345/1/FILE/ROS?r=pbs&ncb=1&ur=FILE&e=300x250:300x250",
+        "uri": "https://ads.us.e-planning.net/hb/1/12345/1/FILE/ROS?e=300x250%3A300x250&ncb=1&r=pbs&ur=FILE",
         "body": {}
       },
       "mockResponse": {
diff --git a/adapters/eplanning/eplanningtest/exemplary/simple-banner.json b/adapters/eplanning/eplanningtest/exemplary/simple-banner.json
index 61b7878a..0403e59a 100644
--- a/adapters/eplanning/eplanningtest/exemplary/simple-banner.json
+++ b/adapters/eplanning/eplanningtest/exemplary/simple-banner.json
@@ -31,7 +31,7 @@
   "httpCalls": [
     {
       "expectedRequest": {
-        "uri": "https://ads.us.e-planning.net/hb/1/12345/1/FILE/ROS?r=pbs&ncb=1&ur=FILE&e=testadun_itco_de:600x300&uid=2154987&ip=123.123.123.123",
+        "uri": "https://ads.us.e-planning.net/hb/1/12345/1/FILE/ROS?e=testadun_itco_de%3A600x300&ip=123.123.123.123&ncb=1&r=pbs&uid=2154987&ur=FILE",
         "body": {}
       },
       "mockResponse": {
diff --git a/adapters/eplanning/eplanningtest/exemplary/two-banners.json b/adapters/eplanning/eplanningtest/exemplary/two-banners.json
index fccc1a30..8c4ca021 100644
--- a/adapters/eplanning/eplanningtest/exemplary/two-banners.json
+++ b/adapters/eplanning/eplanningtest/exemplary/two-banners.json
@@ -39,7 +39,7 @@
     "httpCalls": [
       {
         "expectedRequest": {
-          "uri": "https://ads.us.e-planning.net/hb/1/12345/1/FILE/ROS?r=pbs&ncb=1&ur=FILE&e=testadunitcode:600x300+300x250:300x250&ip=123.123.123.123",
+          "uri": "https://ads.us.e-planning.net/hb/1/12345/1/FILE/ROS?e=testadunitcode%3A600x300%2B300x250%3A300x250&ip=123.123.123.123&ncb=1&r=pbs&ur=FILE",
           "body": {}
         },
         "mockResponse": {
diff --git a/adapters/eplanning/eplanningtest/supplemental/app-domain-and-url-correctly-parsed.json b/adapters/eplanning/eplanningtest/supplemental/app-domain-and-url-correctly-parsed.json
new file mode 100644
index 00000000..e6e25566
--- /dev/null
+++ b/adapters/eplanning/eplanningtest/supplemental/app-domain-and-url-correctly-parsed.json
@@ -0,0 +1,90 @@
+{
+    "mockBidRequest": {
+      "id": "test-request-id",
+      "imp": [
+        {
+          "id": "test-imp-id",
+          "banner": {
+            "w": 600,
+            "h": 300
+          },
+          "ext": {
+            "bidder": {
+              "ci": "12345",
+              "adunit_code": "test_adunitcode"
+            }
+          }
+        }
+      ],
+      "app": {
+        "id": "[a-f0-9]{16}",
+        "name": "MobileExchange",
+        "domain": "sitio.net",
+        "cat":  ["IAB12"],
+        "publisher": {
+          "id": "[a-f0-9]{16}",
+          "name": "mobile - cliente sitio con exchange activo y ad magic"
+        },
+        "bundle": "mx.com.xeu"
+      },
+      "device": {
+        "ua": "test-user-agent",
+        "ip": "123.123.123.123",
+        "ifa": "3B8E2335-Z049",
+        "language": "en",
+        "dnt": 1
+      }
+    },
+  
+    "httpCalls": [
+      {
+        "expectedRequest": {
+          "uri": "https://ads.us.e-planning.net/hb/1/12345/1/mx.com.xeu/ROS?app=1&appid=%5Ba-f0-9%5D%7B16%7D&appn=MobileExchange&e=testadunitcode%3A600x300&ifa=3B8E2335-Z049&ip=123.123.123.123&ncb=1&r=pbs",
+          "body": {}
+        },
+        "mockResponse": {
+          "status": 200,
+          "body": {
+            "sI": { "k": "12345" },
+            "sec": "ROS",
+            "sp": [
+              {
+                "k": "testadunitcode",
+                "a": [{
+                  "i": "123456789abcdef",
+                  "pr": "0.5",
+                  "adm": "<div>test</div>",
+                  "crid": "abcdef123456789",
+                  "id": "adid12345",
+                  "w": 600,
+                  "h": 300
+                }]
+              }
+            ]
+          }
+        }
+      }
+    ],
+  
+    "expectedBidResponses": [
+      {
+        "currency": "USD",
+        "bids": [
+          {
+            "bid": {
+              "id": "123456789abcdef",
+              "impid": "test-imp-id",
+              "price": 0.5,
+              "adm": "<div>test</div>",
+              "adid": "adid12345",
+              "crid": "abcdef123456789",
+              "w": 600,
+              "h": 300
+            },
+            "type": "banner"
+          }
+        ]
+      }
+    ]
+  }
+  
diff --git a/adapters/eplanning/eplanningtest/supplemental/banner-no-size-sends-1x1.json b/adapters/eplanning/eplanningtest/supplemental/banner-no-size-sends-1x1.json
index afa7b953..f1bc29e1 100644
--- a/adapters/eplanning/eplanningtest/supplemental/banner-no-size-sends-1x1.json
+++ b/adapters/eplanning/eplanningtest/supplemental/banner-no-size-sends-1x1.json
@@ -19,7 +19,7 @@
     "httpCalls": [
       {
         "expectedRequest": {
-          "uri": "https://ads.us.e-planning.net/hb/1/12345/1/FILE/ROS?r=pbs&ncb=1&ur=FILE&e=testadunitcodenosize:1x1",
+          "uri": "https://ads.us.e-planning.net/hb/1/12345/1/FILE/ROS?e=testadunitcodenosize%3A1x1&ncb=1&r=pbs&ur=FILE",
           "body": {}
         },
         "mockResponse": {
diff --git a/adapters/eplanning/eplanningtest/supplemental/invalid-response-no-bids.json b/adapters/eplanning/eplanningtest/supplemental/invalid-response-no-bids.json
index 3bf45a16..978989e2 100644
--- a/adapters/eplanning/eplanningtest/supplemental/invalid-response-no-bids.json
+++ b/adapters/eplanning/eplanningtest/supplemental/invalid-response-no-bids.json
@@ -21,7 +21,7 @@
     "httpCalls": [
       {
         "expectedRequest": {
-          "uri": "https://ads.us.e-planning.net/hb/1/12345/1/FILE/ROS?r=pbs&ncb=1&ur=FILE&e=testadunitcode:600x300",
+          "uri": "https://ads.us.e-planning.net/hb/1/12345/1/FILE/ROS?e=testadunitcode%3A600x300&ncb=1&r=pbs&ur=FILE",
           "body": {}
         },
         "mockResponse": {
diff --git a/adapters/eplanning/eplanningtest/supplemental/invalid-response-unmarshall-error.json b/adapters/eplanning/eplanningtest/supplemental/invalid-response-unmarshall-error.json
index e8d88f17..7198f4ee 100644
--- a/adapters/eplanning/eplanningtest/supplemental/invalid-response-unmarshall-error.json
+++ b/adapters/eplanning/eplanningtest/supplemental/invalid-response-unmarshall-error.json
@@ -21,7 +21,7 @@
     "httpCalls": [
       {
         "expectedRequest": {
-          "uri": "https://ads.us.e-planning.net/hb/1/12345/1/FILE/ROS?r=pbs&ncb=1&ur=FILE&e=testadunitcode:600x300",
+          "uri": "https://ads.us.e-planning.net/hb/1/12345/1/FILE/ROS?e=testadunitcode%3A600x300&ncb=1&r=pbs&ur=FILE",
           "body": {}
         },
         "mockResponse": {
diff --git a/adapters/eplanning/eplanningtest/supplemental/server-bad-request.json b/adapters/eplanning/eplanningtest/supplemental/server-bad-request.json
index 421f47ef..938ede62 100644
--- a/adapters/eplanning/eplanningtest/supplemental/server-bad-request.json
+++ b/adapters/eplanning/eplanningtest/supplemental/server-bad-request.json
@@ -21,7 +21,7 @@
     "httpCalls": [
       {
         "expectedRequest": {
-          "uri": "https://ads.us.e-planning.net/hb/1/12345/1/FILE/ROS?r=pbs&ncb=1&ur=FILE&e=testadunitcode:600x300",
+          "uri": "https://ads.us.e-planning.net/hb/1/12345/1/FILE/ROS?e=testadunitcode%3A600x300&ncb=1&r=pbs&ur=FILE",
           "body": {}
         },
         "mockResponse": {
diff --git a/adapters/eplanning/eplanningtest/supplemental/server-error-code.json b/adapters/eplanning/eplanningtest/supplemental/server-error-code.json
index ceec970b..eaa2a677 100644
--- a/adapters/eplanning/eplanningtest/supplemental/server-error-code.json
+++ b/adapters/eplanning/eplanningtest/supplemental/server-error-code.json
@@ -21,7 +21,7 @@
     "httpCalls": [
       {
         "expectedRequest": {
-          "uri": "https://ads.us.e-planning.net/hb/1/12345/1/FILE/ROS?r=pbs&ncb=1&ur=FILE&e=testadunitcode:600x300",
+          "uri": "https://ads.us.e-planning.net/hb/1/12345/1/FILE/ROS?e=testadunitcode%3A600x300&ncb=1&r=pbs&ur=FILE",
           "body": {}
         },
         "mockResponse": {
diff --git a/adapters/eplanning/eplanningtest/supplemental/server-no-content.json b/adapters/eplanning/eplanningtest/supplemental/server-no-content.json
index a2e444a9..d1feb865 100644
--- a/adapters/eplanning/eplanningtest/supplemental/server-no-content.json
+++ b/adapters/eplanning/eplanningtest/supplemental/server-no-content.json
@@ -21,7 +21,7 @@
     "httpCalls": [
       {
         "expectedRequest": {
-          "uri": "https://ads.us.e-planning.net/hb/1/12345/1/FILE/ROS?r=pbs&ncb=1&ur=FILE&e=testadunitcode:600x300",
+          "uri": "https://ads.us.e-planning.net/hb/1/12345/1/FILE/ROS?e=testadunitcode%3A600x300&ncb=1&r=pbs&ur=FILE",
           "body": {}
         },
         "mockResponse": {
diff --git a/adapters/eplanning/eplanningtest/supplemental/site-domain-and-url-correctly-parsed.json b/adapters/eplanning/eplanningtest/supplemental/site-domain-and-url-correctly-parsed.json
index d889f481..2f43b9aa 100644
--- a/adapters/eplanning/eplanningtest/supplemental/site-domain-and-url-correctly-parsed.json
+++ b/adapters/eplanning/eplanningtest/supplemental/site-domain-and-url-correctly-parsed.json
@@ -25,7 +25,7 @@
     "httpCalls": [
       {
         "expectedRequest": {
-          "uri": "https://ads.us.e-planning.net/hb/1/12345/1/www.publisher.com/ROS?r=pbs&ncb=1&ur=http%3A%2F%2Fwww.publisher.com%2Fawesome%2Fsite%3Fwith%3Dsome%26parameters%3Dhere&e=testadunitcode:600x300",
+          "uri": "https://ads.us.e-planning.net/hb/1/12345/1/www.publisher.com/ROS?e=testadunitcode%3A600x300&ncb=1&r=pbs&ur=http%3A%2F%2Fwww.publisher.com%2Fawesome%2Fsite%3Fwith%3Dsome%26parameters%3Dhere",
           "body": {}
         },
         "mockResponse": {
diff --git a/adapters/mobilefuse/mobilefuse_test.go b/adapters/mobilefuse/mobilefuse_test.go
new file mode 100644
index 00000000..15021a06
--- /dev/null
+++ b/adapters/mobilefuse/mobilefuse_test.go
@@ -0,0 +1,10 @@
+package mobilefuse
+
+import (
+	"github.com/prebid/prebid-server/adapters/adapterstest"
+	"testing"
+)
+
+func TestJsonSamples(t *testing.T) {
+	adapterstest.RunJSONBidderTest(t, "mobilefusetest", NewMobileFuseBidder("http://mfx-us-east.mobilefuse.com/openrtb?pub_id={{.PublisherID}}"))
+}
diff --git a/adapters/mobilefuse/mobilefusetest/exemplary/multi-format.json b/adapters/mobilefuse/mobilefusetest/exemplary/multi-format.json
new file mode 100644
index 00000000..49fb8819
--- /dev/null
+++ b/adapters/mobilefuse/mobilefusetest/exemplary/multi-format.json
@@ -0,0 +1,67 @@
+{
+    "mockBidRequest": {
+        "id": "test-request-id",
+        "imp": [
+            {
+                "id": "1",
+                "banner": {
+                    "format": [
+                        {
+                            "w": 320,
+                            "h": 50
+                        }
+                    ]
+                },
+                "video": {
+                    "mimes": [
+                        "video/mp4"
+                    ],
+                    "protocols": [
+                        2,
+                        5
+                    ],
+                    "w": 320,
+                    "h": 480
+                },
+                "ext": {
+                    "bidder": {
+                        "placement_id": 123456,
+                        "pub_id": 1234
+                    }
+                }
+            }
+        ]
+    },
+
+    "httpCalls": [
+        {
+            "expectedRequest": {
+                "uri": "http://mfx-us-east.mobilefuse.com/openrtb?pub_id=1234",
+                "body": {
+                    "id": "test-request-id",
+                    "imp": [
+                        {
+                            "id": "1",
+                            "banner": {
+                                "format": [
+                                    {
+                                        "w": 320,
+                                        "h": 50
+                                    }
+                                ]
+                            },
+                            "tagid": "123456"
+                        }
+                    ]
+                }
+            },
+
+            "mockResponse": {
+                "status": 204,
+                "body": ""
+            }
+        }
+    ],
+
+    "expectedBidResponses": []
+}
diff --git a/adapters/mobilefuse/mobilefusetest/exemplary/multi-imps.json b/adapters/mobilefuse/mobilefusetest/exemplary/multi-imps.json
new file mode 100644
index 00000000..61ac1a3e
--- /dev/null
+++ b/adapters/mobilefuse/mobilefusetest/exemplary/multi-imps.json
@@ -0,0 +1,110 @@
+{
+    "mockBidRequest": {
+        "id": "test-request-id",
+        "imp": [
+            {
+                "id": "1",
+                "banner": {
+                    "format": [
+                        {
+                            "w": 320,
+                            "h": 50
+                        }
+                    ]
+                },
+                "ext": {
+                    "bidder": {
+                        "placement_id": 123456,
+                        "pub_id": 1234
+                    }
+                }
+            },
+            {
+                "id": "2",
+                "banner": {
+                    "format": [
+                        {
+                            "w": 300,
+                            "h": 50
+                        }
+                    ]
+                },
+                "ext": {
+                    "bidder": {
+                        "placement_id": 234567,
+                        "pub_id": 1234
+                    }
+                }
+            }
+        ]
+    },
+
+    "httpCalls": [
+        {
+            "expectedRequest": {
+                "uri": "http://mfx-us-east.mobilefuse.com/openrtb?pub_id=1234",
+                "body": {
+                    "id": "test-request-id",
+                    "imp": [
+                        {
+                            "id": "1",
+                            "banner": {
+                                "format": [
+                                    {
+                                        "w": 320,
+                                        "h": 50
+                                    }
+                                ]
+                            },
+                            "tagid": "123456"
+                        }
+                    ]
+                }
+            },
+
+            "mockResponse": {
+                "status": 200,
+                "body": {
+                    "id": "test-request-id",
+                    "seatbid": [
+                        {
+                            "seat": "mobilefuse",
+                            "bid": [
+                                {
+                                    "id": "test-bid-id",
+                                    "impid": "1",
+                                    "price": 1.50,
+                                    "adm": "some-test-ad",
+                                    "crid": "test-crid",
+                                    "h": 50,
+                                    "w": 320
+                                }
+                            ]
+                        }
+                    ],
+                    "cur": "USD"
+                }
+            }
+        }
+    ],
+
+    "expectedBidResponses": [
+        {
+            "currency": "USD",
+            "bids": [
+                {
+                    "bid": {
+                        "id": "test-bid-id",
+                        "impid": "1",
+                        "price": 1.50,
+                        "adm": "some-test-ad",
+                        "crid": "test-crid",
+                        "w": 320,
+                        "h": 50
+                    },
+                    "type": "banner"
+                }
+            ]
+        }
+    ]
+}
diff --git a/adapters/mobilefuse/mobilefusetest/exemplary/no-bid.json b/adapters/mobilefuse/mobilefusetest/exemplary/no-bid.json
new file mode 100644
index 00000000..cec47a0b
--- /dev/null
+++ b/adapters/mobilefuse/mobilefusetest/exemplary/no-bid.json
@@ -0,0 +1,55 @@
+{
+    "mockBidRequest": {
+        "id": "test-request-id",
+        "imp": [
+            {
+                "id": "1",
+                "banner": {
+                    "format": [
+                        {
+                            "w": 300,
+                            "h": 250
+                        }
+                    ]
+                },
+                "ext": {
+                    "bidder": {
+                        "placement_id": 999999,
+                        "pub_id": 1111
+                    }
+                }
+            }
+        ]
+    },
+
+    "httpCalls": [
+        {
+            "expectedRequest": {
+                "uri": "http://mfx-us-east.mobilefuse.com/openrtb?pub_id=1111",
+                "body": {
+                    "id": "test-request-id",
+                    "imp": [
+                        {
+                            "id": "1",
+                            "banner": {
+                                "format": [
+                                    {
+                                        "w": 300,
+                                        "h": 250
+                                    }
+                                ]
+                            },
+                            "tagid": "999999"
+                        }
+                    ]
+                }
+            },
+            "mockResponse": {
+                "status": 204,
+                "body": {}
+            }
+        }
+    ],
+
+    "expectedBidResponses": []
+}
diff --git a/adapters/mobilefuse/mobilefusetest/exemplary/optional-params.json b/adapters/mobilefuse/mobilefusetest/exemplary/optional-params.json
new file mode 100644
index 00000000..2f71da8f
--- /dev/null
+++ b/adapters/mobilefuse/mobilefusetest/exemplary/optional-params.json
@@ -0,0 +1,56 @@
+{
+    "mockBidRequest": {
+        "id": "test-request-id",
+        "imp": [
+            {
+                "id": "1",
+                "banner": {
+                    "format": [
+                        {
+                            "w": 300,
+                            "h": 250
+                        }
+                    ]
+                },
+                "ext": {
+                    "bidder": {
+                        "placement_id": 999999,
+                        "pub_id": 1111,
+                        "tagid_src": "ext"
+                    }
+                }
+            }
+        ]
+    },
+
+    "httpCalls": [
+        {
+            "expectedRequest": {
+                "uri": "http://mfx-us-east.mobilefuse.com/openrtb?pub_id=1111&tagid_src=ext",
+                "body": {
+                    "id": "test-request-id",
+                    "imp": [
+                        {
+                            "id": "1",
+                            "banner": {
+                                "format": [
+                                    {
+                                        "w": 300,
+                                        "h": 250
+                                    }
+                                ]
+                            },
+                            "tagid": "999999"
+                        }
+                    ]
+                }
+            },
+            "mockResponse": {
+                "status": 204,
+                "body": {}
+            }
+        }
+    ],
+
+    "expectedBidResponses": []
+}
diff --git a/adapters/mobilefuse/mobilefusetest/exemplary/simple-banner.json b/adapters/mobilefuse/mobilefusetest/exemplary/simple-banner.json
new file mode 100644
index 00000000..7db7776d
--- /dev/null
+++ b/adapters/mobilefuse/mobilefusetest/exemplary/simple-banner.json
@@ -0,0 +1,93 @@
+{
+    "mockBidRequest": {
+        "id": "test-request-id",
+        "imp": [
+            {
+                "id": "1",
+                "banner": {
+                    "format": [
+                        {
+                            "w": 320,
+                            "h": 50
+                        }
+                    ]
+                },
+                "ext": {
+                    "bidder": {
+                        "placement_id": 123456,
+                        "pub_id": 1234
+                    }
+                }
+            }
+        ]
+    },
+
+    "httpCalls": [
+        {
+            "expectedRequest": {
+                "uri": "http://mfx-us-east.mobilefuse.com/openrtb?pub_id=1234",
+                "body": {
+                    "id": "test-request-id",
+                    "imp": [
+                        {
+                            "id": "1",
+                            "banner": {
+                                "format": [
+                                    {
+                                        "w": 320,
+                                        "h": 50
+                                    }
+                                ]
+                            },
+                            "tagid": "123456"
+                        }
+                    ]
+                }
+            },
+
+            "mockResponse": {
+                "status": 200,
+                "body": {
+                    "id": "test-request-id",
+                    "seatbid": [
+                        {
+                            "seat": "mobilefuse",
+                            "bid": [
+                                {
+                                    "id": "test-bid-id",
+                                    "impid": "1",
+                                    "price": 1.50,
+                                    "adm": "some-test-ad",
+                                    "crid": "test-crid",
+                                    "h": 50,
+                                    "w": 320
+                                }
+                            ]
+                        }
+                    ],
+                    "cur": "USD"
+                }
+            }
+        }
+    ],
+
+    "expectedBidResponses": [
+        {
+            "currency": "USD",
+            "bids": [
+                {
+                    "bid": {
+                        "id": "test-bid-id",
+                        "impid": "1",
+                        "price": 1.50,
+                        "adm": "some-test-ad",
+                        "crid": "test-crid",
+                        "w": 320,
+                        "h": 50
+                    },
+                    "type": "banner"
+                }
+            ]
+        }
+    ]
+}
diff --git a/adapters/mobilefuse/mobilefusetest/exemplary/simple-video.json b/adapters/mobilefuse/mobilefusetest/exemplary/simple-video.json
new file mode 100644
index 00000000..118c82e1
--- /dev/null
+++ b/adapters/mobilefuse/mobilefusetest/exemplary/simple-video.json
@@ -0,0 +1,96 @@
+{
+    "mockBidRequest": {
+        "id": "test-request-id",
+        "imp": [
+            {
+                "id": "1",
+                "video": {
+                    "mimes": [
+                        "video/mp4"
+                    ],
+                    "protocols": [
+                        2,
+                        5
+                    ],
+                    "w": 320,
+                    "h": 480
+                },
+                "ext": {
+                    "bidder": {
+                        "placement_id": 200000,
+                        "pub_id": 2000
+                    }
+                }
+            }
+        ]
+    },
+    "httpCalls": [
+        {
+            "expectedRequest": {
+                "uri": "http://mfx-us-east.mobilefuse.com/openrtb?pub_id=2000",
+                "body": {
+                    "id": "test-request-id",
+                    "imp": [
+                        {
+                            "id": "1",
+                            "video": {
+                                "mimes": [
+                                    "video/mp4"
+                                ],
+                                "protocols": [
+                                    2,
+                                    5
+                                ],
+                                "w": 320,
+                                "h": 480
+                            },
+                            "tagid": "200000"
+                        }
+                    ]
+                }
+            },
+            "mockResponse": {
+                "status": 200,
+                "body": {
+                    "id": "test-request-id",
+                    "cur": "USD",
+                    "seatbid": [
+                        {
+                            "seat": "mobilefuse",
+                            "bid": [
+                                {
+                                    "id": "test-bid-id",
+                                    "impid": "1",
+                                    "price": 2.50,
+                                    "adm": "some-test-ad",
+                                    "crid": "test-crid",
+                                    "w": 320,
+                                    "h": 480
+                                }
+                            ]
+                        }
+                    ]
+                }
+            }
+        }
+    ],
+    "expectedBidResponses": [
+        {
+            "currency": "USD",
+            "bids": [
+                {
+                    "bid": {
+                        "id": "test-bid-id",
+                        "impid": "1",
+                        "price": 2.50,
+                        "adm": "some-test-ad",
+                        "crid": "test-crid",
+                        "w": 320,
+                        "h": 480
+                    },
+                    "type": "video"
+                }
+            ]
+        }
+    ]
+}
diff --git a/adapters/mobilefuse/mobilefusetest/params/race/banner.json b/adapters/mobilefuse/mobilefusetest/params/race/banner.json
new file mode 100644
index 00000000..8c8d3f2b
--- /dev/null
+++ b/adapters/mobilefuse/mobilefusetest/params/race/banner.json
@@ -0,0 +1,5 @@
+{
+    "placement_id": 123456,
+    "pub_id": 1234,
+    "tagid_src": "ext"
+}
diff --git a/adapters/mobilefuse/mobilefusetest/params/race/video.json b/adapters/mobilefuse/mobilefusetest/params/race/video.json
new file mode 100644
index 00000000..8c8d3f2b
--- /dev/null
+++ b/adapters/mobilefuse/mobilefusetest/params/race/video.json
@@ -0,0 +1,5 @@
+{
+    "placement_id": 123456,
+    "pub_id": 1234,
+    "tagid_src": "ext"
+}
diff --git a/adapters/mobilefuse/mobilefusetest/supplemental/bad-ext-bidder.json b/adapters/mobilefuse/mobilefusetest/supplemental/bad-ext-bidder.json
new file mode 100644
index 00000000..61cf115f
--- /dev/null
+++ b/adapters/mobilefuse/mobilefusetest/supplemental/bad-ext-bidder.json
@@ -0,0 +1,36 @@
+{
+    "expectedMakeRequestsErrors": [
+        {
+            "value": "json: cannot unmarshal string into Go value of type openrtb_ext.ExtImpMobileFuse",
+            "comparison": "literal"
+        }
+    ],
+
+    "mockBidRequest": {
+        "id": "test-request-id",
+        "imp": [
+            {
+                "id": "1",
+                "banner": {
+                    "format": [
+                        {
+                            "w": 300,
+                            "h": 250
+                        },
+                        {
+                            "w": 300,
+                            "h": 600
+                        }
+                    ]
+                },
+                "ext": {
+                    "bidder": "aaa"
+                }
+            }
+        ]
+    },
+
+    "httpCalls": [],
+
+    "expectedBidResponses": []
+}
diff --git a/adapters/mobilefuse/mobilefusetest/supplemental/bad-ext.json b/adapters/mobilefuse/mobilefusetest/supplemental/bad-ext.json
new file mode 100644
index 00000000..f0c23aa2
--- /dev/null
+++ b/adapters/mobilefuse/mobilefusetest/supplemental/bad-ext.json
@@ -0,0 +1,32 @@
+{
+    "expectedMakeRequestsErrors": [
+        {
+            "value": "json: cannot unmarshal string into Go value of type adapters.ExtImpBidder",
+            "comparison": "literal"
+        }
+    ],
+
+    "mockBidRequest": {
+        "id": "test-request-id",
+        "imp": [
+            {
+                "id": "1",
+                "banner": {
+                    "format": [
+                        {
+                            "w": 300,
+                            "h": 250
+                        },
+                        {
+                            "w": 300,
+                            "h": 600
+                        }
+                    ]
+                },
+                "ext": "aaa"
+            }
+        ]
+    },
+
+    "httpCalls": []
+}
diff --git a/adapters/mobilefuse/mobilefusetest/supplemental/bad-status-code.json b/adapters/mobilefuse/mobilefusetest/supplemental/bad-status-code.json
new file mode 100644
index 00000000..5ee94680
--- /dev/null
+++ b/adapters/mobilefuse/mobilefusetest/supplemental/bad-status-code.json
@@ -0,0 +1,62 @@
+{
+    "expectedMakeBidsErrors": [
+        {
+            "value": "Unexpected status code: 400.",
+            "comparison": "literal"
+        }
+    ],
+
+    "mockBidRequest": {
+        "id": "test-request-id",
+        "imp": [
+            {
+                "id": "1",
+                "banner": {
+                    "format": [
+                        {
+                            "w": 300,
+                            "h": 250
+                        }
+                    ]
+                },
+                "ext": {
+                    "bidder": {
+                        "placement_id": 999999,
+                        "pub_id": 1111
+                    }
+                }
+            }
+        ]
+    },
+
+    "httpCalls": [
+        {
+            "expectedRequest": {
+                "uri": "http://mfx-us-east.mobilefuse.com/openrtb?pub_id=1111",
+                "body": {
+                    "id": "test-request-id",
+                    "imp": [
+                        {
+                            "id": "1",
+                            "banner": {
+                                "format": [
+                                    {
+                                        "w": 300,
+                                        "h": 250
+                                    }
+                                ]
+                            },
+                            "tagid": "999999"
+                        }
+                    ]
+                }
+            },
+            "mockResponse": {
+                "status": 400,
+                "body": ""
+            }
+        }
+    ],
+
+    "expectedBidResponses": []
+}
diff --git a/adapters/mobilefuse/mobilefusetest/supplemental/no-imps.json b/adapters/mobilefuse/mobilefusetest/supplemental/no-imps.json
new file mode 100644
index 00000000..eab96d4d
--- /dev/null
+++ b/adapters/mobilefuse/mobilefusetest/supplemental/no-imps.json
@@ -0,0 +1,27 @@
+{
+    "expectedMakeRequestsErrors": [
+        {
+            "value": "No valid imps",
+            "comparison": "literal"
+        }
+    ],
+
+    "mockBidRequest": {
+        "id": "test-request-id",
+        "imp": [
+            {
+                "id": "1",
+                "ext": {
+                    "bidder": {
+                        "placement_id": 999999,
+                        "pub_id": 1111
+                    }
+                }
+            }
+        ]
+    },
+
+    "httpCalls": [],
+
+    "expectedBidResponses": []
+}
diff --git a/adapters/mobilefuse/mobilefusetest/supplemental/server-error-response.json b/adapters/mobilefuse/mobilefusetest/supplemental/server-error-response.json
new file mode 100644
index 00000000..25c4a0bd
--- /dev/null
+++ b/adapters/mobilefuse/mobilefusetest/supplemental/server-error-response.json
@@ -0,0 +1,62 @@
+{
+    "expectedMakeBidsErrors": [
+        {
+            "value": "Unexpected status code: 500.",
+            "comparison": "literal"
+        }
+    ],
+
+    "mockBidRequest": {
+        "id": "test-request-id",
+        "imp": [
+            {
+                "id": "1",
+                "banner": {
+                    "format": [
+                        {
+                            "w": 300,
+                            "h": 250
+                        }
+                    ]
+                },
+                "ext": {
+                    "bidder": {
+                        "placement_id": 999999,
+                        "pub_id": 1111
+                    }
+                }
+            }
+        ]
+    },
+
+    "httpCalls": [
+        {
+            "expectedRequest": {
+                "uri": "http://mfx-us-east.mobilefuse.com/openrtb?pub_id=1111",
+                "body": {
+                    "id": "test-request-id",
+                    "imp": [
+                        {
+                            "id": "1",
+                            "banner": {
+                                "format": [
+                                    {
+                                        "w": 300,
+                                        "h": 250
+                                    }
+                                ]
+                            },
+                            "tagid": "999999"
+                        }
+                    ]
+                }
+            },
+            "mockResponse": {
+                "status": 500,
+                "body": ""
+            }
+        }
+    ],
+
+    "expectedBidResponses": []
+}
diff --git a/adapters/mobilefuse/params_test.go b/adapters/mobilefuse/params_test.go
new file mode 100644
index 00000000..dbfd8894
--- /dev/null
+++ b/adapters/mobilefuse/params_test.go
@@ -0,0 +1,54 @@
+package mobilefuse
+
+import (
+	"encoding/json"
+	"testing"
+
+	"github.com/prebid/prebid-server/openrtb_ext"
+)
+
+func TestValidParams(test *testing.T) {
+	validator, err := openrtb_ext.NewBidderParamsValidator("../../static/bidder-params")
+
+	if err != nil {
+		test.Fatalf("Failed to fetch the json-schemas. %v", err)
+	}
+
+	for _, validParam := range validParams {
+		err := validator.Validate(openrtb_ext.BidderMobileFuse, json.RawMessage(validParam))
+
+		if err != nil {
+			test.Errorf("Schema rejected MobileFuse params: %s\nError: %v", validParam, err)
+		}
+	}
+}
+
+func TestInvalidParams(test *testing.T) {
+	validator, err := openrtb_ext.NewBidderParamsValidator("../../static/bidder-params")
+
+	if err != nil {
+		test.Fatalf("Failed to fetch the json-schemas. %v", err)
+	}
+
+	for _, invalidParam := range invalidParams {
+		err := validator.Validate(openrtb_ext.BidderMobileFuse, json.RawMessage(invalidParam))
+
+		if err == nil {
+			test.Errorf("Schema allowed unexpected params: %s", invalidParam)
+		}
+	}
+}
+
+var validParams = []string{
+	`{"placement_id":123,"pub_id":456}`,
+	`{"placement_id":123,"pub_id":456,"tagid_src":"ext"}`,
+	`{"placement_id":123, "pub_id":456, "tagid_src":""}`,
+}
+
+var invalidParams = []string{
+	`{"placement_id":123}`,
+	`{"pub_id":456}`,
+	`{"placement_id":"123","pub_id":"456"}`,
+	`{"placement_id":123, "placementId":123}`,
+	`{"tagid_src":"ext"}`,
+}
diff --git a/adapters/visx/visxtest/exemplary/simple-banner.json b/adapters/visx/visxtest/exemplary/simple-banner.json
index d4c4ea73..2840521c 100644
--- a/adapters/visx/visxtest/exemplary/simple-banner.json
+++ b/adapters/visx/visxtest/exemplary/simple-banner.json
@@ -77,6 +77,7 @@
                         "amp": 0
                     }
                 },
+                "cur": ["USD"],
                 "imp": [{
                     "id": "test-imp-id",
                     "banner": {
@@ -131,21 +132,21 @@
         "mockResponse": {
             "status": 200,
             "body": {
-                "id": "1519967420713_259406708_583019428",
+                "cur": ["USD"],
                 "seatbid": [
                     {
                         "bid": [
                             {
+                                "crid": "2_260",
                                 "price": 0.500000,
                                 "adm": "some-test-ad",
-                                "impid": "1",
+                                "impid": "test-imp-id",
                                 "auid": 46,
-                                "id": "1",
                                 "h": 250,
                                 "adomain": [
                                     "goodadvertiser.com"
                                 ],
-                                "crid": "11_222222",
+                                "dealid": "test_deal_id",
                                 "w": 300
                             }
                         ],
@@ -157,17 +158,19 @@
     }],
 
     "expectedBidResponses": [{
+        "currency": "USD",
         "bids": [{
             "bid": {
                 "price": 0.5,
                 "adm": "some-test-ad",
-                "impid": "1",
-                "id": "1",
+                "impid": "test-imp-id",
+                "id": "test-request-id",
                 "h": 250,
                 "adomain": [
                     "goodadvertiser.com"
                 ],
-                "crid": "11_222222",
+                "dealid": "test_deal_id",
+                "crid": "2_260",
                 "w": 300
             },
             "type": "banner"
diff --git a/adapters/visx/visxtest/exemplary/with_currency.json b/adapters/visx/visxtest/exemplary/with_currency.json
new file mode 100644
index 00000000..ce7c9283
--- /dev/null
+++ b/adapters/visx/visxtest/exemplary/with_currency.json
@@ -0,0 +1,94 @@
+{
+    "mockBidRequest": {
+        "id": "test-request-id",
+        "site": {
+            "page": "https://good.site/url"
+        },
+        "cur": ["EUR"],
+        "imp": [{
+            "id": "test-imp-id",
+            "banner": {
+                "format": [{
+                    "w": 300,
+                    "h": 250
+                }]
+            },
+            "ext": {
+                "bidder": {
+                    "uid": 7
+                }
+            }
+        }]
+    },
+
+    "httpCalls": [{
+        "expectedRequest": {
+            "uri": "http://localhost/prebid",
+            "body": {
+                "id": "test-request-id",
+                "site": {
+                    "page": "https://good.site/url"
+                },
+                "cur": ["EUR"],
+                "imp": [{
+                    "id": "test-imp-id",
+                    "banner": {
+                        "format": [{
+                            "w": 300,
+                            "h": 250
+                        }]
+                    },
+                    "ext": {
+                        "bidder": {
+                            "uid": 7
+                        }
+                    }
+                }]
+            }
+        },
+        "mockResponse": {
+            "status": 200,
+            "body": {
+                "cur": ["EUR"],
+                "seatbid": [
+                    {
+                        "bid": [
+                            {
+                                "crid": "2_261",
+                                "price": 0.500000,
+                                "adm": "some-test-ad",
+                                "impid": "test-imp-id",
+                                "auid": 46,
+                                "h": 250,
+                                "adomain": [
+                                    "goodadvertiser.com"
+                                ],
+                                "w": 300
+                            }
+                        ],
+                        "seat": "51"
+                    }
+                ]
+            }
+        }
+    }],
+
+    "expectedBidResponses": [{
+        "currency": "EUR",
+        "bids": [{
+            "bid": {
+                "price": 0.5,
+                "adm": "some-test-ad",
+                "impid": "test-imp-id",
+                "id": "test-request-id",
+                "h": 250,
+                "adomain": [
+                    "goodadvertiser.com"
+                ],
+                "crid": "2_261",
+                "w": 300
+            },
+            "type": "banner"
+        }]
+    }]
+}
diff --git a/adapters/visx/visxtest/supplemental/bad_response.json b/adapters/visx/visxtest/supplemental/bad_response.json
index 4ad5c09c..09376f94 100644
--- a/adapters/visx/visxtest/supplemental/bad_response.json
+++ b/adapters/visx/visxtest/supplemental/bad_response.json
@@ -26,6 +26,7 @@
         "uri": "http://localhost/prebid",
         "body": {
           "id": "test-request-id",
+          "cur": ["USD"],
           "imp": [
             {
               "id": "test-imp-id",
@@ -54,7 +55,7 @@
 
   "expectedMakeBidsErrors": [
     {
-      "value": "json: cannot unmarshal string into Go value of type openrtb.BidResponse",
+      "value": "json: cannot unmarshal string into Go value of type visx.visxResponse",
       "comparison": "literal"
     }
   ]
diff --git a/adapters/visx/visxtest/supplemental/status_204.json b/adapters/visx/visxtest/supplemental/status_204.json
index 906d8553..7e0899d9 100644
--- a/adapters/visx/visxtest/supplemental/status_204.json
+++ b/adapters/visx/visxtest/supplemental/status_204.json
@@ -26,6 +26,7 @@
         "uri": "http://localhost/prebid",
         "body": {
           "id": "test-request-id",
+          "cur": ["USD"],
           "imp": [
             {
               "id": "test-imp-id",
diff --git a/adapters/visx/visxtest/supplemental/status_400.json b/adapters/visx/visxtest/supplemental/status_400.json
index dbf2a4d7..82903ca9 100644
--- a/adapters/visx/visxtest/supplemental/status_400.json
+++ b/adapters/visx/visxtest/supplemental/status_400.json
@@ -26,6 +26,7 @@
         "uri": "http://localhost/prebid",
         "body": {
           "id": "test-request-id",
+          "cur": ["USD"],
           "imp": [
             {
               "id": "test-imp-id",
diff --git a/adapters/visx/visxtest/supplemental/status_418.json b/adapters/visx/visxtest/supplemental/status_418.json
index 7619cd6a..86ecf8b0 100644
--- a/adapters/visx/visxtest/supplemental/status_418.json
+++ b/adapters/visx/visxtest/supplemental/status_418.json
@@ -26,6 +26,7 @@
         "uri": "http://localhost/prebid",
         "body": {
           "id": "test-request-id",
+          "cur": ["USD"],
           "imp": [
             {
               "id": "test-imp-id",
diff --git a/endpoints/auction_test.go b/endpoints/auction_test.go
index df91f8ca..3035a6d4 100644
--- a/endpoints/auction_test.go
+++ b/endpoints/auction_test.go
@@ -421,6 +421,10 @@ func (m *auctionMockPermissions) PersonalInfoAllowed(ctx context.Context, bidder
 	return m.allowPI, nil
 }
 
+func (m *auctionMockPermissions) AMPException() bool {
+	return false
+}
+
 func TestBidSizeValidate(t *testing.T) {
 	bids := make(pbs.PBSBidSlice, 0)
 	// bid1 will be rejected due to undefined size when adunit has multiple sizes
diff --git a/endpoints/cookie_sync_test.go b/endpoints/cookie_sync_test.go
index 011cecf3..bb766aa9 100644
--- a/endpoints/cookie_sync_test.go
+++ b/endpoints/cookie_sync_test.go
@@ -257,3 +257,7 @@ func (g *gdprPerms) BidderSyncAllowed(ctx context.Context, bidder openrtb_ext.Bi
 func (g *gdprPerms) PersonalInfoAllowed(ctx context.Context, bidder openrtb_ext.BidderName, PublisherID string, consent string) (bool, error) {
 	return true, nil
 }
+
+func (g *gdprPerms) AMPException() bool {
+	return false
+}
diff --git a/endpoints/openrtb2/auction_test.go b/endpoints/openrtb2/auction_test.go
index 98dfa66d..fdd6b3a4 100644
--- a/endpoints/openrtb2/auction_test.go
+++ b/endpoints/openrtb2/auction_test.go
@@ -602,7 +602,7 @@ func TestStoredRequests(t *testing.T) {
 	// NewMetrics() will create a new go_metrics MetricsEngine, bypassing the need for a crafted configuration set to support it.
 	// As a side effect this gives us some coverage of the go_metrics piece of the metrics engine.
 	theMetrics := pbsmetrics.NewMetrics(metrics.NewRegistry(), openrtb_ext.BidderList(), config.DisabledMetrics{})
-	edep := &endpointDeps{&nobidExchange{}, newParamsValidator(t), &mockStoredReqFetcher{}, empty_fetcher.EmptyFetcher{}, empty_fetcher.EmptyFetcher{}, &config.Configuration{MaxRequestSize: maxSize}, theMetrics, analyticsConf.NewPBSAnalytics(&config.Analytics{}), map[string]string{}, false, []byte{}, openrtb_ext.BidderMap, nil}
+	edep := &endpointDeps{&nobidExchange{}, newParamsValidator(t), &mockStoredReqFetcher{}, empty_fetcher.EmptyFetcher{}, empty_fetcher.EmptyFetcher{}, &config.Configuration{MaxRequestSize: maxSize}, theMetrics, analyticsConf.NewPBSAnalytics(&config.Analytics{}), map[string]string{}, false, []byte{}, openrtb_ext.BidderMap, nil, nil}
 
 	for i, requestData := range testStoredRequests {
 		newRequest, errList := edep.processStoredRequests(context.Background(), json.RawMessage(requestData))
@@ -639,6 +639,7 @@ func TestOversizedRequest(t *testing.T) {
 		[]byte{},
 		openrtb_ext.BidderMap,
 		nil,
+		nil,
 	}
 
 	req := httptest.NewRequest("POST", "/openrtb2/auction", strings.NewReader(reqBody))
@@ -672,6 +673,7 @@ func TestRequestSizeEdgeCase(t *testing.T) {
 		[]byte{},
 		openrtb_ext.BidderMap,
 		nil,
+		nil,
 	}
 
 	req := httptest.NewRequest("POST", "/openrtb2/auction", strings.NewReader(reqBody))
@@ -810,6 +812,7 @@ func TestDisabledBidder(t *testing.T) {
 		[]byte{},
 		openrtb_ext.BidderMap,
 		nil,
+		nil,
 	}
 
 	req := httptest.NewRequest("POST", "/openrtb2/auction", strings.NewReader(reqBody))
@@ -844,6 +847,7 @@ func TestValidateImpExtDisabledBidder(t *testing.T) {
 		[]byte{},
 		openrtb_ext.BidderMap,
 		nil,
+		nil,
 	}
 	errs := deps.validateImpExt(imp, nil, 0)
 	assert.JSONEq(t, `{"appnexus":{"placement_id":555}}`, string(imp.Ext))
@@ -883,6 +887,7 @@ func TestCurrencyTrunc(t *testing.T) {
 		[]byte{},
 		openrtb_ext.BidderMap,
 		nil,
+		nil,
 	}
 
 	ui := uint64(1)
@@ -925,6 +930,7 @@ func TestCCPAInvalidValueWarning(t *testing.T) {
 		[]byte{},
 		openrtb_ext.BidderMap,
 		nil,
+		nil,
 	}
 
 	ui := uint64(1)
diff --git a/endpoints/openrtb2/video_auction_test.go b/endpoints/openrtb2/video_auction_test.go
index 38c9dc3f..5ba34068 100644
--- a/endpoints/openrtb2/video_auction_test.go
+++ b/endpoints/openrtb2/video_auction_test.go
@@ -7,6 +7,7 @@ import (
 	"io/ioutil"
 	"net/http"
 	"net/http/httptest"
+	"regexp"
 	"strings"
 	"testing"
 
@@ -989,10 +990,15 @@ func TestHandleErrorDebugLog(t *testing.T) {
 	err1 := errors.New("Error for testing handleError 1")
 	err2 := errors.New("Error for testing handleError 2")
 	debugLog := exchange.DebugLog{
-		EnableDebug: true,
-		CacheType:   prebid_cache_client.TypeXML,
-		Data:        "test debug data",
-		TTL:         int64(3600),
+		Enabled:   true,
+		CacheType: prebid_cache_client.TypeXML,
+		Data: exchange.DebugData{
+			Request:  "test request string",
+			Headers:  "test headers string",
+			Response: "test response string",
+		},
+		TTL:    int64(3600),
+		Regexp: regexp.MustCompile(`[<>]`),
 	}
 	handleError(&labels, recorder, []error{err1, err2}, &vo, &debugLog)
 
@@ -1047,6 +1053,7 @@ func mockDepsWithMetrics(t *testing.T, ex *mockExchangeVideo) (*endpointDeps, *p
 		[]byte{},
 		openrtb_ext.BidderMap,
 		nil,
+		nil,
 	}
 
 	return edep, theMetrics, mockModule
@@ -1087,6 +1094,7 @@ func mockDeps(t *testing.T, ex *mockExchangeVideo) *endpointDeps {
 		[]byte{},
 		openrtb_ext.BidderMap,
 		ex.cache,
+		regexp.MustCompile(`[<>]`),
 	}
 
 	return edep
@@ -1121,7 +1129,7 @@ type mockExchangeVideo struct {
 
 func (m *mockExchangeVideo) HoldAuction(ctx context.Context, bidRequest *openrtb.BidRequest, ids exchange.IdFetcher, labels pbsmetrics.Labels, categoriesFetcher *stored_requests.CategoryFetcher, debugLog *exchange.DebugLog) (*openrtb.BidResponse, error) {
 	m.lastRequest = bidRequest
-	if debugLog != nil && debugLog.EnableDebug {
+	if debugLog != nil && debugLog.Enabled {
 		m.cache.called = true
 	}
 	ext := []byte(`{"prebid":{"targeting":{"hb_bidder":"appnexus","hb_pb":"20.00","hb_pb_cat_dur":"20.00_395_30s","hb_size":"1x1", "hb_uuid":"837ea3b7-5598-4958-8c45-8e9ef2bf7cc1"},"type":"video"},"bidder":{"appnexus":{"brand_id":1,"auction_id":7840037870526938650,"bidder_id":2,"bid_ad_type":1,"creative_info":{"video":{"duration":30,"mimes":["video\/mp4"]}}}}}`)
diff --git a/endpoints/setuid_test.go b/endpoints/setuid_test.go
index 555f6173..8499ac1c 100644
--- a/endpoints/setuid_test.go
+++ b/endpoints/setuid_test.go
@@ -441,6 +441,10 @@ func (g *mockPermsSetUID) PersonalInfoAllowed(ctx context.Context, bidder openrt
 	return g.allowPI, nil
 }
 
+func (g *mockPermsSetUID) AMPException() bool {
+	return false
+}
+
 func newFakeSyncer(familyName string) usersync.Usersyncer {
 	return fakeSyncer{
 		familyName: familyName,
diff --git a/exchange/auction_test.go b/exchange/auction_test.go
index d23ff03e..e23c45cb 100644
--- a/exchange/auction_test.go
+++ b/exchange/auction_test.go
@@ -3,8 +3,10 @@ package exchange
 import (
 	"context"
 	"encoding/json"
+	"encoding/xml"
 	"fmt"
 	"io/ioutil"
+	"regexp"
 	"strconv"
 	"strings"
 	"testing"
@@ -40,6 +42,61 @@ func TestMakeVASTNurl(t *testing.T) {
 	assert.Equal(t, expect, vast)
 }
 
+func TestBuildCacheString(t *testing.T) {
+	testCases := []struct {
+		description      string
+		debugLog         DebugLog
+		expectedDebugLog DebugLog
+	}{
+		{
+			description: "DebugLog strings should have tags and be formatted",
+			debugLog: DebugLog{
+				Data: DebugData{
+					Request:  "test request string",
+					Headers:  "test headers string",
+					Response: "test response string",
+				},
+				Regexp: regexp.MustCompile(`[<>]`),
+			},
+			expectedDebugLog: DebugLog{
+				Data: DebugData{
+					Request:  "<Request>test request string</Request>",
+					Headers:  "<Headers>test headers string</Headers>",
+					Response: "<Response>test response string</Response>",
+				},
+				Regexp: regexp.MustCompile(`[<>]`),
+			},
+		},
+		{
+			description: "DebugLog strings should have no < or > characters",
+			debugLog: DebugLog{
+				Data: DebugData{
+					Request:  "<test>test request string</test>",
+					Headers:  "test <headers string",
+					Response: "test <response> string",
+				},
+				Regexp: regexp.MustCompile(`[<>]`),
+			},
+			expectedDebugLog: DebugLog{
+				Data: DebugData{
+					Request:  "<Request>testtest request string/test</Request>",
+					Headers:  "<Headers>test headers string</Headers>",
+					Response: "<Response>test response string</Response>",
+				},
+				Regexp: regexp.MustCompile(`[<>]`),
+			},
+		},
+	}
+
+	for _, test := range testCases {
+		test.expectedDebugLog.CacheString = fmt.Sprintf("%s<Log>%s%s%s</Log>", xml.Header, test.expectedDebugLog.Data.Request, test.expectedDebugLog.Data.Headers, test.expectedDebugLog.Data.Response)
+
+		test.debugLog.BuildCacheString()
+
+		assert.Equal(t, test.expectedDebugLog, test.debugLog, test.description)
+	}
+}
+
 // TestCacheJSON executes tests for all the *.json files in cachetest.
 // customcachekey.json test here verifies custom cache key not used for non-vast video
 func TestCacheJSON(t *testing.T) {
diff --git a/exchange/cachetest/debuglog_disabled.json b/exchange/cachetest/debuglog_disabled.json
index 675488c0..88d6332c 100644
--- a/exchange/cachetest/debuglog_disabled.json
+++ b/exchange/cachetest/debuglog_disabled.json
@@ -1,9 +1,13 @@
 {
     "debugLog": {
-        "EnableDebug": false,
+        "Enabled": false,
         "CacheType": "xml",
         "TTL": 3600,
-        "Data": "test debug data"
+        "Data": {
+            "Request": "test request string",
+            "Headers": "test headers string",
+            "Response": "test response string"
+        }
     },
     "bidRequest": {
         "imp": [{
diff --git a/exchange/cachetest/debuglog_enabled.json b/exchange/cachetest/debuglog_enabled.json
index d4486558..670b694f 100644
--- a/exchange/cachetest/debuglog_enabled.json
+++ b/exchange/cachetest/debuglog_enabled.json
@@ -1,9 +1,13 @@
 {    
     "debugLog": {
-        "EnableDebug": true,
+        "Enabled": true,
         "CacheType": "xml",
         "TTL": 3600,
-        "Data": "test debug data"
+        "Data": {
+            "Request": "test request string",
+            "Headers": "test headers string",
+            "Response": "test response string"
+        }
     },
     "bidRequest": {
         "imp": [{
@@ -42,7 +46,7 @@
         }, {
             "Type": "xml",
             "TTLSeconds": 3600,
-            "Data": "test debug data"
+            "Data": "\u003c?xml version=\"1.0\" encoding=\"UTF-8\"?\u003e\n\u003cLog\u003e\u003cRequest\u003etest request string\u003c/Request\u003e\u003cHeaders\u003etest headers string\u003c/Headers\u003e\u003cResponse\u003etest response string\u003c/Response\u003e\u003c/Log\u003e"
         }
     ],
     "defaultTTLs": {
diff --git a/exchange/exchange_test.go b/exchange/exchange_test.go
index 3c1c2f3b..e9b2127e 100644
--- a/exchange/exchange_test.go
+++ b/exchange/exchange_test.go
@@ -740,6 +740,7 @@ func runSpec(t *testing.T, filename string, spec *exchangeSpec) {
 	debugLog := &DebugLog{}
 	if spec.DebugLog != nil {
 		*debugLog = *spec.DebugLog
+		debugLog.Regexp = regexp.MustCompile(`[<>]`)
 	}
 	bid, err := ex.HoldAuction(context.Background(), &spec.IncomingRequest.OrtbRequest, mockIdFetcher(spec.IncomingRequest.Usersyncs), pbsmetrics.Labels{}, &categoriesFetcher, debugLog)
 	responseTimes := extractResponseTimes(t, filename, bid)
@@ -761,13 +762,13 @@ func runSpec(t *testing.T, filename string, spec *exchangeSpec) {
 		}
 	}
 	if spec.DebugLog != nil {
-		if spec.DebugLog.EnableDebug {
-			if len(debugLog.Data) <= len(spec.DebugLog.Data) {
-				t.Errorf("%s: DebugLog was not modified when it should have been", filename)
+		if spec.DebugLog.Enabled {
+			if len(debugLog.Data.Response) == 0 {
+				t.Errorf("%s: DebugLog response was not modified when it should have been", filename)
 			}
 		} else {
-			if !strings.EqualFold(spec.DebugLog.Data, debugLog.Data) {
-				t.Errorf("%s: DebugLog was modified when it shouldn't have been", filename)
+			if len(debugLog.Data.Response) != 0 {
+				t.Errorf("%s: DebugLog response was modified when it shouldn't have been", filename)
 			}
 		}
 	}
diff --git a/exchange/exchangetest/debuglog_disabled.json b/exchange/exchangetest/debuglog_disabled.json
index 0c24c121..3dd04f66 100644
--- a/exchange/exchangetest/debuglog_disabled.json
+++ b/exchange/exchangetest/debuglog_disabled.json
@@ -1,161 +1,230 @@
 {
-    "debugLog": {
-        "EnableDebug": false,
-        "CacheType": "xml",
-        "TTL": 3600,
-        "Data": "test debug data"
-    },
-    "incomingRequest": {
-      "ortbRequest": {
-        "id": "some-request-id",
-        "site": {
-          "page": "test.somepage.com"
-        },
-        "imp": [
-          {
-            "id": "my-imp-id",
-            "video": {
-              "mimes": ["video/mp4"]
-            },
-            "ext": {
-              "appnexus": {
-                "placementId": 1
-              }
-            }
+  "debugLog": {
+    "Enabled": false,
+    "CacheType": "xml",
+    "TTL": 3600,
+    "Data": {
+      "Request": "test request string",
+      "Headers": "test headers string",
+      "Response": ""
+    }
+  },
+  "incomingRequest": {
+    "ortbRequest": {
+      "id": "some-request-id",
+      "site": {
+        "page": "test.somepage.com"
+      },
+      "imp": [
+        {
+          "id": "my-imp-id",
+          "video": {
+            "mimes": [
+              "video/mp4"
+            ]
           },
-          {
-            "id": "imp-id-2",
-            "video": {
-              "mimes": ["video/mp4"]
-            },
-            "ext": {
-              "appnexus": {
-                "placementId": 1
-              }
+          "ext": {
+            "appnexus": {
+              "placementId": 1
             }
           }
-        ],
-        "ext": {
-          "prebid": {
-            "targeting": {
-              "includebrandcategory": {
-                "primaryadserver": 1,
-                "publisher":"",
-                "withcategory": true
-              }
+        },
+        {
+          "id": "imp-id-2",
+          "video": {
+            "mimes": [
+              "video/mp4"
+            ]
+          },
+          "ext": {
+            "appnexus": {
+              "placementId": 1
             }
           }
         }
-      },
-      "usersyncs": {
-        "appnexus": "123"
-      }
-    },
-    "outgoingRequests": {
-      "appnexus": {
-        "mockResponse": {
-          "pbsSeatBid": {
-            "pbsBids": [
-              {
-                "ortbBid": {
-                  "id": "apn-bid",
-                  "impid": "my-imp-id",
-                  "price": 0.3,
-                  "w": 200,
-                  "h": 250,
-                  "crid": "creative-1",
-                  "cat": ["IAB1-1"]
-                },
-                "bidType": "video",
-                "bidVideo": {
-                  "duration": 30,
-                  "PrimaryCategory": ""
-                }
-              },
-              {
-                "ortbBid": {
-                  "id": "apn-other-bid",
-                  "impid": "imp-id-2",
-                  "price": 0.6,
-                  "w": 300,
-                  "h": 500,
-                  "crid": "creative-3",
-                  "cat": ["IAB1-2"]
-                },
-                "bidType": "video"
-              }
-            ]
+      ],
+      "test": 1,
+      "ext": {
+        "prebid": {
+          "targeting": {
+            "includebrandcategory": {
+              "primaryadserver": 1,
+              "publisher": "",
+              "withcategory": true
+            }
           }
         }
       }
     },
-    "response": {
-      "bids": {
-        "id": "some-request-id",
-        "seatbid": [
-          {
-            "seat": "appnexus",
-            "bid": [
-              {
+    "usersyncs": {
+      "appnexus": "123"
+    }
+  },
+  "outgoingRequests": {
+    "appnexus": {
+      "mockResponse": {
+        "pbsSeatBid": {
+          "pbsBids": [
+            {
+              "ortbBid": {
                 "id": "apn-bid",
                 "impid": "my-imp-id",
                 "price": 0.3,
                 "w": 200,
                 "h": 250,
                 "crid": "creative-1",
-                "cat": ["IAB1-1"],
-                "ext": {
-                  "prebid": {
-                    "type": "video",
-                    "targeting": {
-                       "hb_bidder": "appnexus",
-                       "hb_bidder_appnexus": "appnexus",
-                       "hb_cache_host": "www.pbcserver.com",
-                       "hb_cache_host_appnex": "www.pbcserver.com",
-                       "hb_cache_path": "/pbcache/endpoint",
-                       "hb_cache_path_appnex": "/pbcache/endpoint",
-                       "hb_pb": "0.20",
-                       "hb_pb_appnexus": "0.20",
-                       "hb_pb_cat_dur": "0.20_VideoGames_0s",
-                       "hb_pb_cat_dur_appnex": "0.20_VideoGames_0s",
-                       "hb_size": "200x250",
-                       "hb_size_appnexus": "200x250"
-                     }
-                  }
-                }
+                "cat": [
+                  "IAB1-1"
+                ]
               },
-              {
-                "cat": ["IAB1-2"],
-                "crid": "creative-3",
-                "ext": {
-                  "prebid": {
-                    "targeting": {
-                        "hb_bidder": "appnexus",
-                        "hb_bidder_appnexus": "appnexus",
-                        "hb_cache_host": "www.pbcserver.com",
-                        "hb_cache_host_appnex": "www.pbcserver.com",
-                        "hb_cache_path": "/pbcache/endpoint",
-                        "hb_cache_path_appnex": "/pbcache/endpoint",
-                        "hb_pb": "0.50",
-                        "hb_pb_appnexus": "0.50",
-                        "hb_pb_cat_dur": "0.50_HomeDecor_0s",
-                        "hb_pb_cat_dur_appnex": "0.50_HomeDecor_0s",
-                        "hb_size": "300x500",
-                        "hb_size_appnexus": "300x500"
-                      },
-                    "type": "video"
-                  }
-                },
-                "h": 500,
+              "bidType": "video",
+              "bidVideo": {
+                "duration": 30,
+                "PrimaryCategory": ""
+              }
+            },
+            {
+              "ortbBid": {
                 "id": "apn-other-bid",
                 "impid": "imp-id-2",
                 "price": 0.6,
-                "w": 300
+                "w": 300,
+                "h": 500,
+                "crid": "creative-3",
+                "cat": [
+                  "IAB1-2"
+                ]
+              },
+              "bidType": "video"
+            }
+          ]
+        }
+      }
+    }
+  },
+  "response": {
+    "bids": {
+      "id": "some-request-id",
+      "seatbid": [
+        {
+          "seat": "appnexus",
+          "bid": [
+            {
+              "id": "apn-bid",
+              "impid": "my-imp-id",
+              "price": 0.3,
+              "w": 200,
+              "h": 250,
+              "crid": "creative-1",
+              "cat": [
+                "IAB1-1"
+              ],
+              "ext": {
+                "prebid": {
+                  "type": "video",
+                  "targeting": {
+                    "hb_bidder": "appnexus",
+                    "hb_bidder_appnexus": "appnexus",
+                    "hb_cache_host": "www.pbcserver.com",
+                    "hb_cache_host_appnex": "www.pbcserver.com",
+                    "hb_cache_path": "/pbcache/endpoint",
+                    "hb_cache_path_appnex": "/pbcache/endpoint",
+                    "hb_pb": "0.20",
+                    "hb_pb_appnexus": "0.20",
+                    "hb_pb_cat_dur": "0.20_VideoGames_0s",
+                    "hb_pb_cat_dur_appnex": "0.20_VideoGames_0s",
+                    "hb_size": "200x250",
+                    "hb_size_appnexus": "200x250"
+                  }
+                }
               }
-            ]
+            },
+            {
+              "cat": [
+                "IAB1-2"
+              ],
+              "crid": "creative-3",
+              "ext": {
+                "prebid": {
+                  "targeting": {
+                    "hb_bidder": "appnexus",
+                    "hb_bidder_appnexus": "appnexus",
+                    "hb_cache_host": "www.pbcserver.com",
+                    "hb_cache_host_appnex": "www.pbcserver.com",
+                    "hb_cache_path": "/pbcache/endpoint",
+                    "hb_cache_path_appnex": "/pbcache/endpoint",
+                    "hb_pb": "0.50",
+                    "hb_pb_appnexus": "0.50",
+                    "hb_pb_cat_dur": "0.50_HomeDecor_0s",
+                    "hb_pb_cat_dur_appnex": "0.50_HomeDecor_0s",
+                    "hb_size": "300x500",
+                    "hb_size_appnexus": "300x500"
+                  },
+                  "type": "video"
+                }
+              },
+              "h": 500,
+              "id": "apn-other-bid",
+              "impid": "imp-id-2",
+              "price": 0.6,
+              "w": 300
+            }
+          ]
+        }
+      ]
+    },
+    "ext": {
+      "debug": {
+        "httpcalls": {
+          "appnexus": null
+        },
+        "resolvedrequest": {
+          "id": "some-request-id",
+          "imp": [
+            {
+              "id": "my-imp-id",
+              "video": {
+                "mimes": [
+                  "video/mp4"
+                ]
+              },
+              "ext": {
+                "appnexus": {
+                  "placementId": 1
+                }
+              }
+            },
+            {
+              "id": "imp-id-2",
+              "video": {
+                "mimes": [
+                  "video/mp4"
+                ]
+              },
+              "ext": {
+                "appnexus": {
+                  "placementId": 1
+                }
+              }
+            }
+          ],
+          "site": {
+            "page": "test.somepage.com"
+          },
+          "test": 1,
+          "ext": {
+            "prebid": {
+              "targeting": {
+                "includebrandcategory": {
+                  "primaryadserver": 1,
+                  "publisher": "",
+                  "withcategory": true
+                }
+              }
+            }
           }
-        ]
+        }
       }
     }
   }
-  
\ No newline at end of file
+}
\ No newline at end of file
diff --git a/exchange/exchangetest/debuglog_enabled.json b/exchange/exchangetest/debuglog_enabled.json
index 281bf3a1..a70013b4 100644
--- a/exchange/exchangetest/debuglog_enabled.json
+++ b/exchange/exchangetest/debuglog_enabled.json
@@ -1,161 +1,230 @@
 {
-    "debugLog": {
-        "EnableDebug": true,
-        "CacheType": "xml",
-        "TTL": 3600,
-        "Data": "test debug data"
-    },
-    "incomingRequest": {
-      "ortbRequest": {
-        "id": "some-request-id",
-        "site": {
-          "page": "test.somepage.com"
-        },
-        "imp": [
-          {
-            "id": "my-imp-id",
-            "video": {
-              "mimes": ["video/mp4"]
-            },
-            "ext": {
-              "appnexus": {
-                "placementId": 1
-              }
-            }
+  "debugLog": {
+    "Enabled": true,
+    "CacheType": "xml",
+    "TTL": 3600,
+    "Data": {
+      "Request": "test request string",
+      "Headers": "test headers string",
+      "Response": ""
+    }
+  },
+  "incomingRequest": {
+    "ortbRequest": {
+      "id": "some-request-id",
+      "site": {
+        "page": "test.somepage.com"
+      },
+      "imp": [
+        {
+          "id": "my-imp-id",
+          "video": {
+            "mimes": [
+              "video/mp4"
+            ]
           },
-          {
-            "id": "imp-id-2",
-            "video": {
-              "mimes": ["video/mp4"]
-            },
-            "ext": {
-              "appnexus": {
-                "placementId": 1
-              }
+          "ext": {
+            "appnexus": {
+              "placementId": 1
             }
           }
-        ],
-        "ext": {
-          "prebid": {
-            "targeting": {
-              "includebrandcategory": {
-                "primaryadserver": 1,
-                "publisher":"",
-                "withcategory": true
-              }
+        },
+        {
+          "id": "imp-id-2",
+          "video": {
+            "mimes": [
+              "video/mp4"
+            ]
+          },
+          "ext": {
+            "appnexus": {
+              "placementId": 1
             }
           }
         }
-      },
-      "usersyncs": {
-        "appnexus": "123"
-      }
-    },
-    "outgoingRequests": {
-      "appnexus": {
-        "mockResponse": {
-          "pbsSeatBid": {
-            "pbsBids": [
-              {
-                "ortbBid": {
-                  "id": "apn-bid",
-                  "impid": "my-imp-id",
-                  "price": 0.3,
-                  "w": 200,
-                  "h": 250,
-                  "crid": "creative-1",
-                  "cat": ["IAB1-1"]
-                },
-                "bidType": "video",
-                "bidVideo": {
-                  "duration": 30,
-                  "PrimaryCategory": ""
-                }
-              },
-              {
-                "ortbBid": {
-                  "id": "apn-other-bid",
-                  "impid": "imp-id-2",
-                  "price": 0.6,
-                  "w": 300,
-                  "h": 500,
-                  "crid": "creative-3",
-                  "cat": ["IAB1-2"]
-                },
-                "bidType": "video"
-              }
-            ]
+      ],
+      "test": 1,
+      "ext": {
+        "prebid": {
+          "targeting": {
+            "includebrandcategory": {
+              "primaryadserver": 1,
+              "publisher": "",
+              "withcategory": true
+            }
           }
         }
       }
     },
-    "response": {
-      "bids": {
-        "id": "some-request-id",
-        "seatbid": [
-          {
-            "seat": "appnexus",
-            "bid": [
-              {
+    "usersyncs": {
+      "appnexus": "123"
+    }
+  },
+  "outgoingRequests": {
+    "appnexus": {
+      "mockResponse": {
+        "pbsSeatBid": {
+          "pbsBids": [
+            {
+              "ortbBid": {
                 "id": "apn-bid",
                 "impid": "my-imp-id",
                 "price": 0.3,
                 "w": 200,
                 "h": 250,
                 "crid": "creative-1",
-                "cat": ["IAB1-1"],
-                "ext": {
-                  "prebid": {
-                    "type": "video",
-                    "targeting": {
-                       "hb_bidder": "appnexus",
-                       "hb_bidder_appnexus": "appnexus",
-                       "hb_cache_host": "www.pbcserver.com",
-                       "hb_cache_host_appnex": "www.pbcserver.com",
-                       "hb_cache_path": "/pbcache/endpoint",
-                       "hb_cache_path_appnex": "/pbcache/endpoint",
-                       "hb_pb": "0.20",
-                       "hb_pb_appnexus": "0.20",
-                       "hb_pb_cat_dur": "0.20_VideoGames_0s",
-                       "hb_pb_cat_dur_appnex": "0.20_VideoGames_0s",
-                       "hb_size": "200x250",
-                       "hb_size_appnexus": "200x250"
-                     }
-                  }
-                }
+                "cat": [
+                  "IAB1-1"
+                ]
               },
-              {
-                "cat": ["IAB1-2"],
-                "crid": "creative-3",
-                "ext": {
-                  "prebid": {
-                    "targeting": {
-                        "hb_bidder": "appnexus",
-                        "hb_bidder_appnexus": "appnexus",
-                        "hb_cache_host": "www.pbcserver.com",
-                        "hb_cache_host_appnex": "www.pbcserver.com",
-                        "hb_cache_path": "/pbcache/endpoint",
-                        "hb_cache_path_appnex": "/pbcache/endpoint",
-                        "hb_pb": "0.50",
-                        "hb_pb_appnexus": "0.50",
-                        "hb_pb_cat_dur": "0.50_HomeDecor_0s",
-                        "hb_pb_cat_dur_appnex": "0.50_HomeDecor_0s",
-                        "hb_size": "300x500",
-                        "hb_size_appnexus": "300x500"
-                      },
-                    "type": "video"
-                  }
-                },
-                "h": 500,
+              "bidType": "video",
+              "bidVideo": {
+                "duration": 30,
+                "PrimaryCategory": ""
+              }
+            },
+            {
+              "ortbBid": {
                 "id": "apn-other-bid",
                 "impid": "imp-id-2",
                 "price": 0.6,
-                "w": 300
+                "w": 300,
+                "h": 500,
+                "crid": "creative-3",
+                "cat": [
+                  "IAB1-2"
+                ]
+              },
+              "bidType": "video"
+            }
+          ]
+        }
+      }
+    }
+  },
+  "response": {
+    "bids": {
+      "id": "some-request-id",
+      "seatbid": [
+        {
+          "seat": "appnexus",
+          "bid": [
+            {
+              "id": "apn-bid",
+              "impid": "my-imp-id",
+              "price": 0.3,
+              "w": 200,
+              "h": 250,
+              "crid": "creative-1",
+              "cat": [
+                "IAB1-1"
+              ],
+              "ext": {
+                "prebid": {
+                  "type": "video",
+                  "targeting": {
+                    "hb_bidder": "appnexus",
+                    "hb_bidder_appnexus": "appnexus",
+                    "hb_cache_host": "www.pbcserver.com",
+                    "hb_cache_host_appnex": "www.pbcserver.com",
+                    "hb_cache_path": "/pbcache/endpoint",
+                    "hb_cache_path_appnex": "/pbcache/endpoint",
+                    "hb_pb": "0.20",
+                    "hb_pb_appnexus": "0.20",
+                    "hb_pb_cat_dur": "0.20_VideoGames_0s",
+                    "hb_pb_cat_dur_appnex": "0.20_VideoGames_0s",
+                    "hb_size": "200x250",
+                    "hb_size_appnexus": "200x250"
+                  }
+                }
               }
-            ]
+            },
+            {
+              "cat": [
+                "IAB1-2"
+              ],
+              "crid": "creative-3",
+              "ext": {
+                "prebid": {
+                  "targeting": {
+                    "hb_bidder": "appnexus",
+                    "hb_bidder_appnexus": "appnexus",
+                    "hb_cache_host": "www.pbcserver.com",
+                    "hb_cache_host_appnex": "www.pbcserver.com",
+                    "hb_cache_path": "/pbcache/endpoint",
+                    "hb_cache_path_appnex": "/pbcache/endpoint",
+                    "hb_pb": "0.50",
+                    "hb_pb_appnexus": "0.50",
+                    "hb_pb_cat_dur": "0.50_HomeDecor_0s",
+                    "hb_pb_cat_dur_appnex": "0.50_HomeDecor_0s",
+                    "hb_size": "300x500",
+                    "hb_size_appnexus": "300x500"
+                  },
+                  "type": "video"
+                }
+              },
+              "h": 500,
+              "id": "apn-other-bid",
+              "impid": "imp-id-2",
+              "price": 0.6,
+              "w": 300
+            }
+          ]
+        }
+      ]
+    },
+    "ext": {
+      "debug": {
+        "httpcalls": {
+          "appnexus": null
+        },
+        "resolvedrequest": {
+          "id": "some-request-id",
+          "imp": [
+            {
+              "id": "my-imp-id",
+              "video": {
+                "mimes": [
+                  "video/mp4"
+                ]
+              },
+              "ext": {
+                "appnexus": {
+                  "placementId": 1
+                }
+              }
+            },
+            {
+              "id": "imp-id-2",
+              "video": {
+                "mimes": [
+                  "video/mp4"
+                ]
+              },
+              "ext": {
+                "appnexus": {
+                  "placementId": 1
+                }
+              }
+            }
+          ],
+          "site": {
+            "page": "test.somepage.com"
+          },
+          "test": 1,
+          "ext": {
+            "prebid": {
+              "targeting": {
+                "includebrandcategory": {
+                  "primaryadserver": 1,
+                  "publisher": "",
+                  "withcategory": true
+                }
+              }
+            }
           }
-        ]
+        }
       }
     }
   }
-  
\ No newline at end of file
+}
\ No newline at end of file
diff --git a/exchange/utils_test.go b/exchange/utils_test.go
index edbe04a0..53d6b85c 100644
--- a/exchange/utils_test.go
+++ b/exchange/utils_test.go
@@ -31,6 +31,10 @@ func (p *permissionsMock) PersonalInfoAllowed(ctx context.Context, bidder openrt
 	return false, nil
 }
 
+func (p *permissionsMock) AMPException() bool {
+	return false
+}
+
 func assertReq(t *testing.T, reqByBidders map[openrtb_ext.BidderName]*openrtb.BidRequest,
 	applyCOPPA bool, consentedVendors map[string]bool) {
 	// assert individual bidder requests
diff --git a/privacy/ccpa/policy_test.go b/privacy/ccpa/policy_test.go
index 54613c89..740f95a8 100644
--- a/privacy/ccpa/policy_test.go
+++ b/privacy/ccpa/policy_test.go
@@ -71,6 +71,17 @@ func TestRead(t *testing.T) {
 			},
 			expectedError: true,
 		},
+		{
+			description: "Injection Attack",
+			request: &openrtb.BidRequest{
+				Regs: &openrtb.Regs{
+					Ext: json.RawMessage(`{"us_privacy":"1YYY\"},\"oops\":\"malicious\",\"p\":{\"p\":\""}`),
+				},
+			},
+			expectedPolicy: Policy{
+				Value: "1YYY\"},\"oops\":\"malicious\",\"p\":{\"p\":\"",
+			},
+		},
 	}
 
 	for _, test := range testCases {
@@ -138,6 +149,32 @@ func TestWrite(t *testing.T) {
 				Ext: json.RawMessage(`malformed`)}},
 			expectedError: true,
 		},
+		{
+			description: "Injection Attack With Nil Request Regs Object",
+			policy:      Policy{Value: "1YYY\"},\"oops\":\"malicious\",\"p\":{\"p\":\""},
+			request:     &openrtb.BidRequest{},
+			expected: &openrtb.BidRequest{Regs: &openrtb.Regs{
+				Ext: json.RawMessage(`{"us_privacy":"1YYY\"},\"oops\":\"malicious\",\"p\":{\"p\":\""}`),
+			}},
+		},
+		{
+			description: "Injection Attack With Nil Request Regs Ext Object",
+			policy:      Policy{Value: "1YYY\"},\"oops\":\"malicious\",\"p\":{\"p\":\""},
+			request:     &openrtb.BidRequest{Regs: &openrtb.Regs{}},
+			expected: &openrtb.BidRequest{Regs: &openrtb.Regs{
+				Ext: json.RawMessage(`{"us_privacy":"1YYY\"},\"oops\":\"malicious\",\"p\":{\"p\":\""}`),
+			}},
+		},
+		{
+			description: "Injection Attack With Existing Request Regs Ext Object",
+			policy:      Policy{Value: "1YYY\"},\"oops\":\"malicious\",\"p\":{\"p\":\""},
+			request: &openrtb.BidRequest{Regs: &openrtb.Regs{
+				Ext: json.RawMessage(`{"existing":"any"}`),
+			}},
+			expected: &openrtb.BidRequest{Regs: &openrtb.Regs{
+				Ext: json.RawMessage(`{"existing":"any","us_privacy":"1YYY\"},\"oops\":\"malicious\",\"p\":{\"p\":\""}`),
+			}},
+		},
 	}
 
 	for _, test := range testCases {
diff --git a/privacy/enforcement_test.go b/privacy/enforcement_test.go
index c7433f8b..25e08b5e 100644
--- a/privacy/enforcement_test.go
+++ b/privacy/enforcement_test.go
@@ -51,12 +51,13 @@ func TestAny(t *testing.T) {
 
 func TestApply(t *testing.T) {
 	testCases := []struct {
-		description             string
-		enforcement             Enforcement
-		expectedDeviceIPv6      ScrubStrategyIPV6
-		expectedDeviceGeo       ScrubStrategyGeo
-		expectedUserDemographic ScrubStrategyDemographic
-		expectedUserGeo         ScrubStrategyGeo
+		description        string
+		enforcement        Enforcement
+		ampGDPRException   bool
+		expectedDeviceIPv6 ScrubStrategyIPV6
+		expectedDeviceGeo  ScrubStrategyGeo
+		expectedUser       ScrubStrategyUser
+		expectedUserGeo    ScrubStrategyGeo
 	}{
 		{
 			description: "All Enforced",
@@ -65,10 +66,11 @@ func TestApply(t *testing.T) {
 				COPPA: true,
 				GDPR:  true,
 			},
-			expectedDeviceIPv6:      ScrubStrategyIPV6Lowest32,
-			expectedDeviceGeo:       ScrubStrategyGeoFull,
-			expectedUserDemographic: ScrubStrategyDemographicAgeAndGender,
-			expectedUserGeo:         ScrubStrategyGeoFull,
+			ampGDPRException:   false,
+			expectedDeviceIPv6: ScrubStrategyIPV6Lowest32,
+			expectedDeviceGeo:  ScrubStrategyGeoFull,
+			expectedUser:       ScrubStrategyUserIDAndDemographic,
+			expectedUserGeo:    ScrubStrategyGeoFull,
 		},
 		{
 			description: "CCPA Only",
@@ -77,10 +79,11 @@ func TestApply(t *testing.T) {
 				COPPA: false,
 				GDPR:  false,
 			},
-			expectedDeviceIPv6:      ScrubStrategyIPV6Lowest16,
-			expectedDeviceGeo:       ScrubStrategyGeoReducedPrecision,
-			expectedUserDemographic: ScrubStrategyDemographicNone,
-			expectedUserGeo:         ScrubStrategyGeoReducedPrecision,
+			ampGDPRException:   false,
+			expectedDeviceIPv6: ScrubStrategyIPV6Lowest16,
+			expectedDeviceGeo:  ScrubStrategyGeoReducedPrecision,
+			expectedUser:       ScrubStrategyUserID,
+			expectedUserGeo:    ScrubStrategyGeoReducedPrecision,
 		},
 		{
 			description: "COPPA Only",
@@ -89,10 +92,11 @@ func TestApply(t *testing.T) {
 				COPPA: true,
 				GDPR:  false,
 			},
-			expectedDeviceIPv6:      ScrubStrategyIPV6Lowest32,
-			expectedDeviceGeo:       ScrubStrategyGeoFull,
-			expectedUserDemographic: ScrubStrategyDemographicAgeAndGender,
-			expectedUserGeo:         ScrubStrategyGeoFull,
+			ampGDPRException:   false,
+			expectedDeviceIPv6: ScrubStrategyIPV6Lowest32,
+			expectedDeviceGeo:  ScrubStrategyGeoFull,
+			expectedUser:       ScrubStrategyUserIDAndDemographic,
+			expectedUserGeo:    ScrubStrategyGeoFull,
 		},
 		{
 			description: "GDPR Only",
@@ -101,10 +105,50 @@ func TestApply(t *testing.T) {
 				COPPA: false,
 				GDPR:  true,
 			},
-			expectedDeviceIPv6:      ScrubStrategyIPV6Lowest16,
-			expectedDeviceGeo:       ScrubStrategyGeoReducedPrecision,
-			expectedUserDemographic: ScrubStrategyDemographicNone,
-			expectedUserGeo:         ScrubStrategyGeoReducedPrecision,
+			ampGDPRException:   false,
+			expectedDeviceIPv6: ScrubStrategyIPV6Lowest16,
+			expectedDeviceGeo:  ScrubStrategyGeoReducedPrecision,
+			expectedUser:       ScrubStrategyUserID,
+			expectedUserGeo:    ScrubStrategyGeoReducedPrecision,
+		},
+		{
+			description: "GDPR Only, ampGDPRException",
+			enforcement: Enforcement{
+				CCPA:  false,
+				COPPA: false,
+				GDPR:  true,
+			},
+			ampGDPRException:   true,
+			expectedDeviceIPv6: ScrubStrategyIPV6Lowest16,
+			expectedDeviceGeo:  ScrubStrategyGeoReducedPrecision,
+			expectedUser:       ScrubStrategyUserNone,
+			expectedUserGeo:    ScrubStrategyGeoReducedPrecision,
+		},
+		{
+			description: "CCPA Only, ampGDPRException",
+			enforcement: Enforcement{
+				CCPA:  true,
+				COPPA: false,
+				GDPR:  false,
+			},
+			ampGDPRException:   true,
+			expectedDeviceIPv6: ScrubStrategyIPV6Lowest16,
+			expectedDeviceGeo:  ScrubStrategyGeoReducedPrecision,
+			expectedUser:       ScrubStrategyUserID,
+			expectedUserGeo:    ScrubStrategyGeoReducedPrecision,
+		},
+		{
+			description: "COPPA and GDPR, ampGDPRException",
+			enforcement: Enforcement{
+				CCPA:  false,
+				COPPA: true,
+				GDPR:  true,
+			},
+			ampGDPRException:   true,
+			expectedDeviceIPv6: ScrubStrategyIPV6Lowest32,
+			expectedDeviceGeo:  ScrubStrategyGeoFull,
+			expectedUser:       ScrubStrategyUserIDAndDemographic,
+			expectedUserGeo:    ScrubStrategyGeoFull,
 		},
 	}
 
@@ -118,9 +162,9 @@ func TestApply(t *testing.T) {
 
 		m := &mockScrubber{}
 		m.On("ScrubDevice", req.Device, test.expectedDeviceIPv6, test.expectedDeviceGeo).Return(replacedDevice).Once()
-		m.On("ScrubUser", req.User, test.expectedUserDemographic, test.expectedUserGeo).Return(replacedUser).Once()
+		m.On("ScrubUser", req.User, test.expectedUser, test.expectedUserGeo).Return(replacedUser).Once()
 
-		test.enforcement.apply(req, m)
+		test.enforcement.apply(req, test.ampGDPRException, m)
 
 		m.AssertExpectations(t)
 		assert.Same(t, replacedDevice, req.Device, "Device")
@@ -138,7 +182,7 @@ func TestApplyNoneApplicable(t *testing.T) {
 		COPPA: false,
 		GDPR:  false,
 	}
-	enforcement.apply(req, m)
+	enforcement.apply(req, false, m)
 
 	m.AssertNotCalled(t, "ScrubDevice")
 	m.AssertNotCalled(t, "ScrubUser")
@@ -148,7 +192,7 @@ func TestApplyNil(t *testing.T) {
 	m := &mockScrubber{}
 
 	enforcement := Enforcement{}
-	enforcement.apply(nil, m)
+	enforcement.apply(nil, false, m)
 
 	m.AssertNotCalled(t, "ScrubDevice")
 	m.AssertNotCalled(t, "ScrubUser")
@@ -163,7 +207,7 @@ func (m *mockScrubber) ScrubDevice(device *openrtb.Device, ipv6 ScrubStrategyIPV
 	return args.Get(0).(*openrtb.Device)
 }
 
-func (m *mockScrubber) ScrubUser(user *openrtb.User, demographic ScrubStrategyDemographic, geo ScrubStrategyGeo) *openrtb.User {
-	args := m.Called(user, demographic, geo)
+func (m *mockScrubber) ScrubUser(user *openrtb.User, strategy ScrubStrategyUser, geo ScrubStrategyGeo) *openrtb.User {
+	args := m.Called(user, strategy, geo)
 	return args.Get(0).(*openrtb.User)
 }
diff --git a/privacy/gdpr/policy_test.go b/privacy/gdpr/policy_test.go
index 00b97644..c9bf10cd 100644
--- a/privacy/gdpr/policy_test.go
+++ b/privacy/gdpr/policy_test.go
@@ -42,7 +42,7 @@ func TestWrite(t *testing.T) {
 			request: &openrtb.BidRequest{User: &openrtb.User{
 				Ext: json.RawMessage(`{"existing":"any"}`)}},
 			expected: &openrtb.BidRequest{User: &openrtb.User{
-				Ext: json.RawMessage(`{"existing":"any","consent":"anyConsent"}`)}},
+				Ext: json.RawMessage(`{"consent":"anyConsent","existing":"any"}`)}},
 		},
 		{
 			description: "Enabled With Existing Request User Ext Object - Overwrites",
@@ -50,7 +50,7 @@ func TestWrite(t *testing.T) {
 			request: &openrtb.BidRequest{User: &openrtb.User{
 				Ext: json.RawMessage(`{"existing":"any","consent":"toBeOverwritten"}`)}},
 			expected: &openrtb.BidRequest{User: &openrtb.User{
-				Ext: json.RawMessage(`{"existing":"any","consent":"anyConsent"}`)}},
+				Ext: json.RawMessage(`{"consent":"anyConsent","existing":"any"}`)}},
 		},
 		{
 			description: "Enabled With Existing Malformed Request User Ext Object",
@@ -59,6 +59,32 @@ func TestWrite(t *testing.T) {
 				Ext: json.RawMessage(`malformed`)}},
 			expectedError: true,
 		},
+		{
+			description: "Injection Attack With Nil Request User Object",
+			policy:      Policy{Consent: "BONV8oqONXwgmADACHENAO7pqzAAppY\"},\"oops\":\"malicious\",\"p\":{\"p\":\""},
+			request:     &openrtb.BidRequest{},
+			expected: &openrtb.BidRequest{User: &openrtb.User{
+				Ext: json.RawMessage(`{"consent":"BONV8oqONXwgmADACHENAO7pqzAAppY\"},\"oops\":\"malicious\",\"p\":{\"p\":\""}`),
+			}},
+		},
+		{
+			description: "Injection Attack With Nil Request User Ext Object",
+			policy:      Policy{Consent: "BONV8oqONXwgmADACHENAO7pqzAAppY\"},\"oops\":\"malicious\",\"p\":{\"p\":\""},
+			request:     &openrtb.BidRequest{User: &openrtb.User{}},
+			expected: &openrtb.BidRequest{User: &openrtb.User{
+				Ext: json.RawMessage(`{"consent":"BONV8oqONXwgmADACHENAO7pqzAAppY\"},\"oops\":\"malicious\",\"p\":{\"p\":\""}`),
+			}},
+		},
+		{
+			description: "Injection Attack With Existing Request User Ext Object",
+			policy:      Policy{Consent: "BONV8oqONXwgmADACHENAO7pqzAAppY\"},\"oops\":\"malicious\",\"p\":{\"p\":\""},
+			request: &openrtb.BidRequest{User: &openrtb.User{
+				Ext: json.RawMessage(`{"existing":"any"}`),
+			}},
+			expected: &openrtb.BidRequest{User: &openrtb.User{
+				Ext: json.RawMessage(`{"consent":"BONV8oqONXwgmADACHENAO7pqzAAppY\"},\"oops\":\"malicious\",\"p\":{\"p\":\"","existing":"any"}`),
+			}},
+		},
 	}
 
 	for _, test := range testCases {
diff --git a/privacy/scrubber_test.go b/privacy/scrubber_test.go
index 2d5ee667..a6c4bde3 100644
--- a/privacy/scrubber_test.go
+++ b/privacy/scrubber_test.go
@@ -252,7 +252,7 @@ func TestScrubUser(t *testing.T) {
 
 	testCases := []struct {
 		expected    *openrtb.User
-		demographic ScrubStrategyDemographic
+		user        ScrubStrategyUser
 		geo         ScrubStrategyGeo
 		description string
 	}{
@@ -265,8 +265,8 @@ func TestScrubUser(t *testing.T) {
 				Gender:   "",
 				Geo:      &openrtb.Geo{},
 			},
-			demographic: ScrubStrategyDemographicAgeAndGender,
-			geo:         ScrubStrategyGeoFull,
+			user: ScrubStrategyUserIDAndDemographic,
+			geo:  ScrubStrategyGeoFull,
 		},
 		{
 			description: "Demographic Age And Gender & Geo Reduced",
@@ -283,11 +283,11 @@ func TestScrubUser(t *testing.T) {
 					ZIP:   "some zip",
 				},
 			},
-			demographic: ScrubStrategyDemographicAgeAndGender,
-			geo:         ScrubStrategyGeoReducedPrecision,
+			user: ScrubStrategyUserIDAndDemographic,
+			geo:  ScrubStrategyGeoReducedPrecision,
 		},
 		{
-			description: "Demographic Age And Gender & Geo None",
+			description: "Demographic Full & Geo None",
 			expected: &openrtb.User{
 				ID:       "",
 				BuyerUID: "",
@@ -301,11 +301,11 @@ func TestScrubUser(t *testing.T) {
 					ZIP:   "some zip",
 				},
 			},
-			demographic: ScrubStrategyDemographicAgeAndGender,
-			geo:         ScrubStrategyGeoNone,
+			user: ScrubStrategyUserIDAndDemographic,
+			geo:  ScrubStrategyGeoNone,
 		},
 		{
-			description: "Demographic None & Geo Full",
+			description: "Demographic Buyer ID & Geo Full",
 			expected: &openrtb.User{
 				ID:       "",
 				BuyerUID: "",
@@ -313,11 +313,11 @@ func TestScrubUser(t *testing.T) {
 				Gender:   "anyGender",
 				Geo:      &openrtb.Geo{},
 			},
-			demographic: ScrubStrategyDemographicNone,
-			geo:         ScrubStrategyGeoFull,
+			user: ScrubStrategyUserID,
+			geo:  ScrubStrategyGeoFull,
 		},
 		{
-			description: "Demographic None & Geo Reduced",
+			description: "Demographic Buyer ID & Geo Reduced",
 			expected: &openrtb.User{
 				ID:       "",
 				BuyerUID: "",
@@ -331,11 +331,29 @@ func TestScrubUser(t *testing.T) {
 					ZIP:   "some zip",
 				},
 			},
-			demographic: ScrubStrategyDemographicNone,
-			geo:         ScrubStrategyGeoReducedPrecision,
+			user: ScrubStrategyUserID,
+			geo:  ScrubStrategyGeoReducedPrecision,
 		},
 		{
 			description: "Demographic None & Geo None",
+			expected: &openrtb.User{
+				ID:       "anyID",
+				BuyerUID: "anyBuyerUID",
+				Yob:      42,
+				Gender:   "anyGender",
+				Geo: &openrtb.Geo{
+					Lat:   123.456,
+					Lon:   678.89,
+					Metro: "some metro",
+					City:  "some city",
+					ZIP:   "some zip",
+				},
+			},
+			user: ScrubStrategyUserNone,
+			geo:  ScrubStrategyGeoNone,
+		},
+		{
+			description: "Demographic BuyerIDOnly & Geo None",
 			expected: &openrtb.User{
 				ID:       "",
 				BuyerUID: "",
@@ -349,19 +367,19 @@ func TestScrubUser(t *testing.T) {
 					ZIP:   "some zip",
 				},
 			},
-			demographic: ScrubStrategyDemographicNone,
-			geo:         ScrubStrategyGeoNone,
+			user: ScrubStrategyUserID,
+			geo:  ScrubStrategyGeoNone,
 		},
 	}
 
 	for _, test := range testCases {
-		result := NewScrubber().ScrubUser(user, test.demographic, test.geo)
+		result := NewScrubber().ScrubUser(user, test.user, test.geo)
 		assert.Equal(t, test.expected, result, test.description)
 	}
 }
 
 func TestScrubUserNil(t *testing.T) {
-	result := NewScrubber().ScrubUser(nil, ScrubStrategyDemographicNone, ScrubStrategyGeoNone)
+	result := NewScrubber().ScrubUser(nil, ScrubStrategyUserNone, ScrubStrategyGeoNone)
 	assert.Nil(t, result)
 }
 
diff --git a/usersync/usersyncers/syncer_test.go b/usersync/usersyncers/syncer_test.go
index 49ce85d0..20fce80c 100755
--- a/usersync/usersyncers/syncer_test.go
+++ b/usersync/usersyncers/syncer_test.go
@@ -78,14 +78,16 @@ func TestNewSyncerMap(t *testing.T) {
 
 	adaptersWithoutSyncers := map[openrtb_ext.BidderName]bool{
 		openrtb_ext.BidderAdgeneration: true,
+		openrtb_ext.BidderAdhese:       true,
 		openrtb_ext.BidderAdoppler:     true,
 		openrtb_ext.BidderApplogy:      true,
-		openrtb_ext.BidderTappx:        true,
+		openrtb_ext.BidderKidoz:        true,
 		openrtb_ext.BidderKubient:      true,
+		openrtb_ext.BidderMobileFuse:   true,
+		openrtb_ext.BidderOrbidder:     true,
 		openrtb_ext.BidderPubnative:    true,
-		openrtb_ext.BidderKidoz:        true,
+		openrtb_ext.BidderTappx:        true,
 		openrtb_ext.BidderYeahmobi:     true,
-		openrtb_ext.BidderOrbidder:     true,
 	}
 
 	for bidder, config := range cfg.Adapters {