Make auction creative rewriting optional#916
Conversation
Allow operators to retain sanitizer-accepted external URLs in POST /auction adm while preserving mandatory server-side sanitization and the existing default behavior.
aram356
left a comment
There was a problem hiding this comment.
Summary
The runtime branch itself is well scoped: sanitization remains mandatory, rewriting is gated after sanitization, both modes have substantive tests, and /first-party/proxy remains independent. I am requesting changes for rollback compatibility and two operator-facing configuration/privacy contracts described in the inline comments.
Non-blocking
🏕 camp site
- Update the internal auction README:
crates/trusted-server-core/src/auction/README.md:139,:251, and:382still describe creative rewriting as unconditional. Please consider updating those passages to distinguish mandatory sanitization from default-on, configurable rewriting.
📌 out of scope
- Track the pre-existing
iframe[srcdoc]sanitizer gap: the generic handler atcrates/trusted-server-core/src/creative.rs:385neither removes nor recursively sanitizessrcdoc, while the normal renderer grantsallow-scriptsandallow-same-originatcrates/trusted-server-js/lib/src/core/render.ts:14. This exists at the base SHA and should not block this PR, but it deserves a security follow-up that removessrcdocand adds regression coverage through both rewrite modes.
👍 praise
- The implementation keeps sanitization strictly before the configuration branch, avoids logging creative contents, covers default/disabled behavior and legacy blob loading, and verifies that proxy HTML/CSS rewriting is independent. No new dependency, OS API, or WASM-incompatible construct is introduced.
CI Status
- fmt and all adapter/target clippy checks: PASS
- Rust tests and builds (Fastly, Axum, Cloudflare, Spin, parity, CLI): PASS
- JS formatting and Vitest: PASS
- integration, browser, Fastly EC lifecycle, and CodeQL checks: PASS
|
Implemented and pushed the requested review fixes in Also addressed the non-blocking internal auction README cleanup. The pre-existing nested Validation completed locally:
|
prk-Jr
left a comment
There was a problem hiding this comment.
Summary
Adds a default-true [auction].rewrite_creatives setting: creative sanitization for POST /auction stays mandatory, while first-party resource/click rewriting and TSJS injection become optional. Backward-compatibility handling (default omitted from serialized/legacy blobs, explicit false preserved, legacy schema round-trip, EdgeZero env-overlay pre-existing-leaf quirk) is thorough and well tested. No blocking issues found.
Non-blocking
⛏ nitpick
- "unre-written" wording:
docs/guide/configuration.mdanddocs/guide/creative-processing.mdrepeat the hyphenation "unre-written" (e.g. "sanitized but unre-written HTML") several times. Suggest "not rewritten" or "unrewritten" for readability — no behavior impact.
👍 praise
- Backward-compatibility test coverage: the
skip_serializing_ifdefault-omission design plus tests acrosssettings.rs(TOML omitted/explicit-false),config_payload.rs(legacy JSON blob round-trip and legacy-schema deserialization), and the new CLI integration test (config_env_overlay.rs, proving the EdgeZero "env overlay only overrides pre-existing leaves" quirk is handled) directly cover the real rollback/migration edge cases operators will hit. Theproxy.rstest proving/first-party/proxyrewriting stays independent of the new flag is a good isolation check too.
📝 note
- Change table is stale: the PR description's file table lists 12 files; the actual diff touches 16, including
creative.rs(see inline comment — an independent exclusion-matching bug fix bundled here),crates/trusted-server-cli/tests/config_env_overlay.rs,crates/trusted-server-core/src/auction/README.md, anddocs/guide/cli.md. Worth syncing the table with the real diff before merge.
CI Status
- fmt: PASS
- clippy (fastly/axum/cloudflare native+wasm/spin native+wasm): PASS
- rust tests (fastly/axum/cloudflare/spin/cross-adapter parity/CLI): PASS
- js tests (vitest): PASS
- js/docs format: PASS
| || lower.starts_with("about:") | ||
| { | ||
| // Match exclusions against the same absolute URL used for rewriting. | ||
| if settings.rewrite.is_excluded(&absolute) { |
There was a problem hiding this comment.
📌 out of scope — This changes to_abs() to build the absolute URL before calling settings.rewrite.is_excluded(), instead of checking exclusion on the raw token. That's a real behavior fix: is_excluded() calls url::Url::parse(), which fails on a schemeless string like //trusted-cdn.example.com/x.js, so previously an exclude_domains entry never actually excluded a creative URL that used the protocol-relative form — it got rewritten anyway despite being configured as excluded. It's well tested (new cases around line 1317+), but it's an independent bug fix bundled into the "make rewriting optional" PR and isn't mentioned in the Change table. Worth calling out explicitly in the PR description since it changes existing exclusion behavior on main for protocol-relative URLs.
| let sanitized = creative::sanitize_creative_html(raw_creative); | ||
| let rewritten = creative::rewrite_creative_html(settings, &sanitized); | ||
| let sanitized_len = sanitized.len(); | ||
| let rewrite_creatives = settings.auction.rewrite_creatives; |
There was a problem hiding this comment.
🤔 thinking — settings.auction.rewrite_creatives is read fresh on every iteration of the per-bid loop; it's loop-invariant for the whole response. Trivial cost, not blocking — just flagging that hoisting it above the for loop would be marginally cleaner.
Summary
[auction].rewrite_creativessetting for winning-bidadmreturned byPOST /auction./first-party/proxyHTML/CSS rewriting and document the behavior and privacy trade-offs.Changes
crates/trusted-server-core/src/auction_config_types.rsrewrite_creativeswith explicit serde and Rust defaults oftrue.crates/trusted-server-core/src/auction/orchestrator.rscrates/trusted-server-core/src/auction/formats.rscrates/trusted-server-core/src/auction/endpoints.rs/auctionresponses.crates/trusted-server-core/src/settings.rscrates/trusted-server-core/src/config_payload.rscrates/trusted-server-core/src/proxy.rstrusted-server.example.tomldocs/guide/configuration.mddocs/guide/auction-orchestration.mddocs/guide/creative-processing.mdCHANGELOG.mdCloses
Closes #914
Test plan
cargo test-fastly && cargo test-axumcargo clippy-fastly && cargo clippy-axumcargo fmt --all -- --checkcd crates/trusted-server-js/lib && npx vitest runcd crates/trusted-server-js/lib && npm run formatcd docs && npm run formatcargo build --package trusted-server-adapter-fastly --release --target wasm32-wasip1fastly compute servecargo doc --package trusted-server-core --no-deps --all-features(completed with existing unrelated rustdoc warnings)Checklist
unwrap()in production code — useexpect("should ...")logmacros (notprintln!)