Summary
When the Prebid external_bundle_url serves bytes that don't match the configured external_bundle_sha256 / external_bundle_sri, Trusted Server proxies the mismatched bundle anyway with a 200, stamps it with the config-derived ?v=/ETag, and injects the config-derived integrity="sha384-…" into the <script> tag. The browser then blocks the script via Subresource Integrity, Prebid.js never initializes, pbjs.que is never drained, requestBids never runs — no auction, no ads — and nothing on the server side reports a problem.
This is a silent, total Prebid outage that presents as "ads don't render / we don't call the auction", with the only signal buried in the browser console (Failed to find a valid digest in the 'integrity' attribute … the resource has been blocked). TS should not fail silently here — it should detect the mismatch and return an error.
Mechanism
- The browser
integrity= value comes straight from config (external_bundle_sri), injected verbatim in external_bundle_script_tag — prebid.rs:701-711.
- The
?v= cache key comes from external_bundle_sha256 — prebid.rs:694-698.
- The bundle is proxied from
external_bundle_url via proxy_request in handle_external_bundle — prebid.rs:832-862 — and the response is returned without hashing/verifying the proxied bytes against external_bundle_sha256.
external_bundle_sha256 is only format-validated at config load (validate_external_bundle_sha256, prebid.rs:448); it is never checked against the actual served content. Likewise external_bundle_sri is only format-validated (prebid.rs:534).
So the served bytes and the two configured hashes are fully decoupled. If the deployed bundle at external_bundle_url is rebuilt/replaced without regenerating the config (both hashes are meant to be produced together by build-prebid-external.mjs → the ts CLI, prebid_bundle.rs:492-493), the config drifts and every browser silently blocks Prebid.
Reproduction (observed on a live deployment)
Config carried one build's hashes while external_bundle_url served a different build. Fetching the proxied bundle:
- Served bundle (real Prebid.js, ~347 KB): actual
sha256 = <A>, actual sha384 = <B>
- Config / injected:
external_bundle_sha256 = <C> (used for ?v= and ETag: sha256:<C>), external_bundle_sri = sha384-<D>
<A> != <C> and <B> != <D> — yet the response was 200 application/javascript with ETag: sha256:<C> (a label that doesn't match the actual bytes) and integrity="sha384-<D>".
Result: browser SRI check fails → Prebid.js blocked → pbjs.que never drains → no auction.
Expected behavior — do not fail silently
When external_bundle_sha256 (and/or external_bundle_sri) is configured, TS should verify the proxied bundle bytes against it and refuse to serve a mismatched bundle:
- In
handle_external_bundle, compute the digest of the proxied response body and compare to external_bundle_sha256.
- On mismatch: return an error response (e.g.
502/503 with Cache-Control: no-store) instead of the mismatched bytes, and log::error! a clear diagnostic (expected vs actual digest, external_bundle_url). Never serve bytes that won't match the integrity the page advertises.
- Optionally derive
external_bundle_sri from the same verified bytes rather than trusting an independent config value that can drift, or at least assert external_bundle_sri is consistent with external_bundle_sha256's source at config-load time.
Implementation note
handle_external_bundle currently proxies .with_streaming() (prebid.rs:856), so full-body verification means either buffering the bundle (it's a static, cacheable asset — acceptable) or hashing on the fly and failing the response if the final digest mismatches. Either is preferable to silently shipping a browser-blocked bundle.
Impact
- Complete client-side Prebid outage (no bids, no auction, no ads) whenever the external bundle and config hashes drift.
- Extremely hard to diagnose: server returns
200, x-ts-version present, ad stack injected — the only symptom is a browser-console SRI error and an undrained pbjs.que.
Severity
High — a single stale config value silently disables the entire client-side auction in every browser, with no server-side signal.
Summary
When the Prebid
external_bundle_urlserves bytes that don't match the configuredexternal_bundle_sha256/external_bundle_sri, Trusted Server proxies the mismatched bundle anyway with a200, stamps it with the config-derived?v=/ETag, and injects the config-derivedintegrity="sha384-…"into the<script>tag. The browser then blocks the script via Subresource Integrity, Prebid.js never initializes,pbjs.queis never drained,requestBidsnever runs — no auction, no ads — and nothing on the server side reports a problem.This is a silent, total Prebid outage that presents as "ads don't render / we don't call the auction", with the only signal buried in the browser console (
Failed to find a valid digest in the 'integrity' attribute … the resource has been blocked). TS should not fail silently here — it should detect the mismatch and return an error.Mechanism
integrity=value comes straight from config (external_bundle_sri), injected verbatim inexternal_bundle_script_tag—prebid.rs:701-711.?v=cache key comes fromexternal_bundle_sha256—prebid.rs:694-698.external_bundle_urlviaproxy_requestinhandle_external_bundle—prebid.rs:832-862— and the response is returned without hashing/verifying the proxied bytes againstexternal_bundle_sha256.external_bundle_sha256is only format-validated at config load (validate_external_bundle_sha256,prebid.rs:448); it is never checked against the actual served content. Likewiseexternal_bundle_sriis only format-validated (prebid.rs:534).So the served bytes and the two configured hashes are fully decoupled. If the deployed bundle at
external_bundle_urlis rebuilt/replaced without regenerating the config (both hashes are meant to be produced together bybuild-prebid-external.mjs→ thetsCLI,prebid_bundle.rs:492-493), the config drifts and every browser silently blocks Prebid.Reproduction (observed on a live deployment)
Config carried one build's hashes while
external_bundle_urlserved a different build. Fetching the proxied bundle:sha256 = <A>, actualsha384 = <B>external_bundle_sha256 = <C>(used for?v=andETag: sha256:<C>),external_bundle_sri = sha384-<D><A> != <C>and<B> != <D>— yet the response was200 application/javascriptwithETag: sha256:<C>(a label that doesn't match the actual bytes) andintegrity="sha384-<D>".Result: browser SRI check fails → Prebid.js blocked →
pbjs.quenever drains → no auction.Expected behavior — do not fail silently
When
external_bundle_sha256(and/orexternal_bundle_sri) is configured, TS should verify the proxied bundle bytes against it and refuse to serve a mismatched bundle:handle_external_bundle, compute the digest of the proxied response body and compare toexternal_bundle_sha256.502/503withCache-Control: no-store) instead of the mismatched bytes, andlog::error!a clear diagnostic (expected vs actual digest,external_bundle_url). Never serve bytes that won't match theintegritythe page advertises.external_bundle_srifrom the same verified bytes rather than trusting an independent config value that can drift, or at least assertexternal_bundle_sriis consistent withexternal_bundle_sha256's source at config-load time.Implementation note
handle_external_bundlecurrently proxies.with_streaming()(prebid.rs:856), so full-body verification means either buffering the bundle (it's a static, cacheable asset — acceptable) or hashing on the fly and failing the response if the final digest mismatches. Either is preferable to silently shipping a browser-blocked bundle.Impact
200,x-ts-versionpresent, ad stack injected — the only symptom is a browser-console SRI error and an undrainedpbjs.que.Severity
High — a single stale config value silently disables the entire client-side auction in every browser, with no server-side signal.