Summary
Creative sanitization runs unconditionally on every markup bid. sanitize_creative_html removes script, object, embed, base, meta, form, link, style and noscript together with their inner content. For a script-based creative — the majority of programmatic display — that leaves nothing renderable.
The failure is silent. What usually survives is a tracking pixel, so adm is non-empty, the client renders it "successfully", and the ad server records a normal impression. The slot is simply blank.
Measured impact
From Processed creative debug logs over 291 creative deliveries on a live publisher:
| metric |
value |
| bytes removed, median |
43% |
| bytes removed, mean |
34% |
| creatives reduced >80% |
29 / 291 |
| creatives reduced to <500 bytes |
20 |
Per bidder (median / max loss):
| bidder |
n |
median |
max |
| A |
1 |
100% |
100% |
| B |
3 |
98% |
98% |
| C |
43 |
76% |
100% |
| D |
2 |
63% |
100% |
| E |
5 |
35% |
68% |
| F |
220 |
7% |
45% |
Bidder C is 43 creatives losing three quarters of their bytes. Bidder A loses everything. All of it without an error.
Why sanitization is load-bearing today
Creative markup renders in an iframe built by createAdIframe, whose sandbox is:
const CREATIVE_SANDBOX_TOKENS = [
'allow-forms', 'allow-popups', 'allow-popups-to-escape-sandbox',
'allow-same-origin', 'allow-scripts', 'allow-top-navigation-by-user-activation',
];
allow-same-origin together with allow-scripts removes the sandbox's origin isolation entirely. The codebase already documents this, on ADM_IFRAME_SANDBOX:
Deliberately excludes allow-same-origin: combined with allow-scripts on srcdoc (or first-party src) content, that pair effectively removes the sandbox's origin isolation and would let SSP-provided markup run with the publisher origin's privileges.
APS_RENDERER_SANDBOX omits it for the same reason. CREATIVE_SANDBOX_TOKENS is the only one that grants it, and render.ts states the consequence plainly — "Server-side sanitization is the primary defense against malicious markup; the sandbox provides defense-in-depth."
So sanitization cannot simply be removed: it is currently compensating for a sandbox that provides no boundary.
Note also that the client-side sanitizeCreativeHtml is not a sanitizer — it validates type and emptiness and returns the input unchanged (removedCount is always 0). It provides no backstop.
Proposed change
- Add
auction.sanitize_creatives so sanitization can be disabled where creatives render in a foreign-origin frame — for example the Prebid Universal Creative inside the ad server's iframe, which rewrite_creative_html already documents as the target context. There the markup cannot reach the publisher origin and stripping it only destroys creatives.
- Remove
allow-same-origin from CREATIVE_SANDBOX_TOKENS, bringing it in line with its two siblings, so the origin boundary is enforced by isolation rather than by an optional transform.
These must ship together: disabling sanitization while the sandbox still grants allow-same-origin would open both holes at once.
Acceptance criteria
Summary
Creative sanitization runs unconditionally on every markup bid.
sanitize_creative_htmlremovesscript,object,embed,base,meta,form,link,styleandnoscripttogether with their inner content. For a script-based creative — the majority of programmatic display — that leaves nothing renderable.The failure is silent. What usually survives is a tracking pixel, so
admis non-empty, the client renders it "successfully", and the ad server records a normal impression. The slot is simply blank.Measured impact
From
Processed creativedebug logs over 291 creative deliveries on a live publisher:Per bidder (median / max loss):
Bidder C is 43 creatives losing three quarters of their bytes. Bidder A loses everything. All of it without an error.
Why sanitization is load-bearing today
Creative markup renders in an iframe built by
createAdIframe, whose sandbox is:allow-same-origintogether withallow-scriptsremoves the sandbox's origin isolation entirely. The codebase already documents this, onADM_IFRAME_SANDBOX:APS_RENDERER_SANDBOXomits it for the same reason.CREATIVE_SANDBOX_TOKENSis the only one that grants it, andrender.tsstates the consequence plainly — "Server-side sanitization is the primary defense against malicious markup; the sandbox provides defense-in-depth."So sanitization cannot simply be removed: it is currently compensating for a sandbox that provides no boundary.
Note also that the client-side
sanitizeCreativeHtmlis not a sanitizer — it validates type and emptiness and returns the input unchanged (removedCountis always 0). It provides no backstop.Proposed change
auction.sanitize_creativesso sanitization can be disabled where creatives render in a foreign-origin frame — for example the Prebid Universal Creative inside the ad server's iframe, whichrewrite_creative_htmlalready documents as the target context. There the markup cannot reach the publisher origin and stripping it only destroys creatives.allow-same-originfromCREATIVE_SANDBOX_TOKENS, bringing it in line with its two siblings, so the origin boundary is enforced by isolation rather than by an optional transform.These must ship together: disabling sanitization while the sandbox still grants
allow-same-originwould open both holes at once.Acceptance criteria
rewrite_creativesallow-same-originwithallow-scriptssanitize_*coverage is retained