Support Sourcepoint GPP consent for EC generation#642
Support Sourcepoint GPP consent for EC generation#642ChristianPavilonis wants to merge 21 commits intofeature/edge-cookies-finalfrom
Conversation
7009838 to
43df212
Compare
0e5c07c to
a8b5b1c
Compare
aram356
left a comment
There was a problem hiding this comment.
Summary
Sourcepoint GPP consent is a small, well-motivated slice — the GPP-US decoder, the allows_ec_creation gating branch, and their tests are solid. But as shipped, the feature is non-functional (the JS module is never served to browsers), the PR fails CI, and it bundles several large unrelated changes that should be separate PRs.
Blocking
🔧 wrench
-
Sourcepoint JS module is built but never served.
crates/trusted-server-core/src/integrations/registry.rs:790-792hardcodesJS_ALWAYS = &["creative"]. The design spec (docs/superpowers/specs/2026-04-15-sourcepoint-gpp-consent-design.md:38) says the integration follows the same "JS-only, no Rust registration" pattern ascreative, but that pattern requiresJS_ALWAYSto list the module.integrations/mod.rshas nosourcepoint::registerandJS_ALWAYShas no"sourcepoint"entry, soIntegrationRegistry::js_module_ids()will never include it —/static/tsjs=…will never concatenatetsjs-sourcepoint.jsinto a served bundle. End-to-end, no browser will ever executemirrorSourcepointConsent(). Fix: add"sourcepoint"toJS_ALWAYSand a test asserting it ships injs_module_ids_immediate(). -
Orphaned dead-code file
crates/trusted-server-core/src/ec/admin.rs. 380 lines implementingPOST /_ts/admin/partners/register, butec/mod.rscontains nopub mod admin;declaration (grep -rn "mod admin\|ec::admin::" crates/returns zero hits). The base branch (feature/edge-cookies-final) replaced the KV-backed partner registry with config-based partners in commit049a501e; that change droppedadmin.rsbut the rebase on this branch reinstated it unreferenced. Fix: deletecrates/trusted-server-core/src/ec/admin.rs. -
Clippy
-D dead_codefailures block CI. Three dead symbols introduced by the same rebase and carrying no callers:crates/trusted-server-core/src/platform/test_support.rs:174—recorded_request_headersmethodcrates/trusted-server-core/src/platform/test_support.rs:336—build_services_with_http_clientfunctioncrates/integration-tests/tests/common/runtime.rs:56—PartnerRegistrationFailedvariant (this is the actual failing CI log message)
Confirmed locally:
cargo clippy --workspace --all-targets --all-features -- -D warningsfails in the PR's worktree. Fix: delete them all — nothing references any of the three. -
Undisclosed scope: creative iframe sandbox lockdown.
crates/js/lib/src/core/render.ts:7-18removesallow-scripts,allow-same-origin, andallow-formsfrom the creative iframesandbox=attribute, andcrates/trusted-server-core/src/creative.rs:361-367strips<iframe>elements entirely during HTML sanitization. Together, any creative relying on JavaScript (viewability pixels, click tracking, rich media, VPAID) or on third-party ad-tag iframes will stop rendering. This is a major ad-tech behavioral change wholly unrelated to Sourcepoint GPP consent, and it is not mentioned in the PR body. It needs its own PR with a threat model, product sign-off, and a rollout plan. Fix: extract these two changes to a dedicated PR.
❓ question
- Why is Prebid User ID Module support shipping inside this PR? The title and body are "Support Sourcepoint GPP consent for EC generation," but the diff also contains ~132 lines of
build-all.mjsUser-ID-submodule generation, a new_user_ids.generated.ts, ~290 lines of new Prebid test coverage, a 212-line design spec (specs/2026-04-16-prebid-user-id-module-design.md), and a 599-line implementation plan. Two independent features in one review is hard to evaluate. Can Prebid User ID Module move to its own PR?
Non-blocking
📌 out of scope
- No re-mirror after mid-session CMP interaction.
mirrorSourcepointConsent()runs once when the module is parsed. If a user opens the Sourcepoint CMP and updates consent mid-session,__gpp/__gpp_sidwon't reflect the new state until the next page load — meaning the same session can continue to block EC creation even after consent is granted. Follow-up: subscribe to__gppapievents (or astoragelistener) and re-run on change.
|
Addressed the requested review feedback in 3bbdb1d. Summary:
Validation run locally:
I also replied to and resolved the inline threads above. |
3bbdb1d to
8a6df3a
Compare
9261993 to
d8c943d
Compare
aram356
left a comment
There was a problem hiding this comment.
Summary
Follow-up review focused on areas not covered in the prior pass. Sourcepoint flow (mirror + GPP US decoding + EC gating) is functional and well-tested, but two doc/file-stale issues from the scoping commit (8a6df3af) need correction before merge, and the always-shipped Sourcepoint module has cross-CMP failure modes worth designing around.
PR effective scope note: vs main the diff is 80 commits / 111 files, because the intended base (feature/edge-cookies-final) has merged. The Sourcepoint-specific changes are a small subset (~14 files); reviewers landing on this PR via GitHub should read the description with that in mind.
Blocking
🔧 wrench
- Stale Prebid User ID docs reference removed
TSJS_PREBID_USER_IDSenv var (docs/guide/integrations/prebid.md:226-261) _user_ids.generated.tsclaims to be auto-generated but is now hand-edited (crates/js/lib/src/integrations/prebid/_user_ids.generated.ts:1)
Non-blocking
🤔 thinking
- Sourcepoint hardcoded as always-shipped will clobber
__gppset by other CMPs (registry.rs:792+sourcepoint/index.ts:60-72) - First page load race: mirror runs before Sourcepoint CMP populates localStorage (
sourcepoint/index.ts:91-93) - TCF presence silently overrides explicit GPP US
sale_opt_out=Yesin US states (consent/mod.rs:506-515) - Session-scoped cookie + run-once mirror leaves stale
__gppafter mid-session retraction (sourcepoint/index.ts:39)
♻️ refactor
- Stale comment displaced by
us_sale_opt_outinsertion (consent/gpp.rs:74-75) - Make clearing logic CMP-safe by tracking write source (
sourcepoint/index.ts:42-46)
⛏ nitpick
- Unused/inconsistent default export (
sourcepoint/index.ts:95)
CI Status
- fmt: PASS
- clippy: PASS
- rust tests: PASS (1001 lib + 21 misc)
- js tests: PASS (294)
Prebid's liveIntentIdSystem.js uses a dynamic require() inside a build-flag-guarded branch that their gulp pipeline dead-codes via constant folding. esbuild leaves the require() in the output, causing ReferenceError: require is not defined at browser runtime. Remove from the bundle until we add an esbuild resolver plugin (or switch to Prebid's own build pipeline) — tracked as a follow-up in the design spec.
Introduces TSJS_PREBID_USER_IDS env var (mirroring TSJS_PREBID_ADAPTERS) to control which Prebid User ID submodules are bundled. The hardcoded imports in index.ts are replaced with a generated file written by build-all.mjs at build time, defaulting to the same 13-submodule set. - build-all.mjs: generatePrebidUserIds() validates names, denylists liveIntentIdSystem, and writes _user_ids.generated.ts. Existence check also probes dist/src/public/ to handle modules shipped as .ts in sources (sharedIdSystem). - index.ts: replaces 13 hardcoded submodule imports with import './_user_ids.generated' - _user_ids.generated.ts: committed default with all 13 submodules - Tests: updated mocks and regression guard; added 9 syncPrebidEidsCookie behavior tests - Docs: new "User ID Modules" section in prebid.md with TSJS_PREBID_USER_IDS usage; spec follow-up #1 marked complete
__gpp and __gpp_sid are read by the Rust server over HTTPS; they must be Secure. Also sets Max-Age=86400 (matching ts-eids) so stale consent state doesn't outlast the session, and replaces the legacy expires= deletion pattern with Max-Age=0.
… and Move logging initialization into Fastly adapter (#610) * Rename crates to trusted-server-core and trusted-server-adapter-fastly Rename crates/common → crates/trusted-server-core and crates/fastly → crates/trusted-server-adapter-fastly following the EdgeZero naming convention. Add EdgeZero workspace dependencies pinned to rev 170b74b. Update all references across docs, CI workflows, scripts, agent files, and configuration. * Add platform abstraction layer with traits and RuntimeServices Introduces trusted-server-core::platform with PlatformConfigStore, PlatformSecretStore, PlatformKvStore, PlatformBackend, PlatformHttpClient, and PlatformGeo traits alongside ClientInfo, PlatformError, and RuntimeServices. Wires the Fastly adapter implementations and threads RuntimeServices into route_request. Moves GeoInfo to platform/types as platform-neutral data and adds geo_from_fastly for field mapping. * Address platform layer review feedback - Defer KV store opening: replace early error return with a local UnavailableKvStore fallback so routes that do not need synthetic ID access succeed when the KV store is missing or temporarily unavailable - Use ConfigStore::try_open + try_get and SecretStore::try_get throughout FastlyPlatformConfigStore and FastlyPlatformSecretStore to honour the Result contract instead of panicking on open/lookup failure - Encapsulate RuntimeServices service fields as pub(crate) with public getter methods (config_store, secret_store, backend, http_client, geo) and a pub new() constructor; adapter updated to use new() - Reference #487 in FastlyPlatformHttpClient stub (PR 6 implements it) - Remove unused KvPage re-export from platform/mod.rs - Use super::KvHandle shorthand in RuntimeServices::kv_handle() * Reject host strings containing control characters in BackendConfig * Fix clippy error * Validate scheme and host for control characters in BackendConfig * Address review findings on platform abstraction layer * Address review findings on platform abstraction layer * Add config store read path and storage module split - Split fastly_storage.rs into storage/{config_store,secret_store,api_client,mod}.rs - Add PlatformConfigStore read path via FastlyPlatformConfigStore::get using ConfigStore::try_open/try_get - Add PlatformError::NotImplemented variant; stub write methods on FastlyPlatformConfigStore and FastlyPlatformSecretStore - Add StoreName/StoreId newtypes with From<String>, From<&str>, AsRef<str> - Add UnavailableKvStore to core platform module - Add RuntimeServicesBuilder replacing 7-arg constructor - Migrate get_active_jwks and handle_trusted_server_discovery to use &RuntimeServices - Update call sites in signing.rs, rotation.rs, main.rs - Add success-path test for handle_trusted_server_discovery using StubJwksConfigStore - Fix test_parse_cookies_to_jar_empty typo (was emtpy) * Harden legacy config-store reads and align Fastly adapter stubs * Address storage review feedback * Resolved github-advanced-security bot problems * Address PR review feedback on platform abstraction layer - Make StoreName and StoreId inner fields private; From/AsRef provide all needed construction and access - Add #[deprecated] to GeoInfo::from_request with #[allow(deprecated)] at the three legacy call sites to track migration progress - Enumerate the six platform traits in the platform module doc comment - Extract backend_config_from_spec helper to remove duplicate BackendConfig construction in predict_name and ensure - Replace .into_iter().collect() with .to_vec() on secret plaintext bytes - Remove unused bytes dependency from trusted-server-adapter-fastly - Add comment on SecretStore::open clarifying it already returns Result (unlike ConfigStore::open which panics) * Add PR 4 design spec for secret store trait (read-only) * Clarify test scope and deferred branches in PR 4 spec * Add implementation plan for PR 4 secret store trait * Add test for get_secret_bytes open-failure path * Add NotImplemented tests for FastlyPlatformSecretStore write stubs * Inline StoreId binding and add section comment in write-stub tests * Remove plan * Add PR 6 design spec for backend and HTTP client traits * Address spec review findings on PR 6 design * Implement PlatformHttpClient and thread RuntimeServices through proxy layer - Add PlatformHttpClient trait with send(), send_async(), and select() methods - Add PlatformBackend trait with predict_name() and ensure() methods - Add PlatformResponse wrapper around EdgeZero HTTP responses - Add PlatformPendingRequest and PlatformSelectResult for auction fan-out - Thread RuntimeServices through IntegrationProxy::handle(), IntegrationRegistry::handle_proxy(), and all first-party proxy endpoints so handlers can reach the HTTP client - Add StubHttpClient and StubBackend test stubs with build_services_with_http_client helper - Add proxy_request_calls_platform_http_client_send integration test - Fix proxy_with_redirects to stay within 7-arg clippy limit via ProxyRequestHeaders struct - Document Body::Stream limitation in edge_request_to_fastly with warning log - Document intentional duplication of platform_response_to_fastly across proxy and orchestrator - Remove spec file (promoted to plan + implementation) * Address pr review findings * Resolve pr review findings * Add PR7 design spec for geo lookup + client info extract-once Documents the call site migration plan: five Fastly SDK extraction points in trusted-server-core replaced by RuntimeServices::client_info reads, following Phase 1 injection pattern from the EdgeZero migration design. * Fix spec review issues in PR7 design doc - Correct erroneous claim about generate_synthetic_id being called twice via DeviceInfo; it is called once (line 91 for fresh_id), DeviceInfo.ip is a separate req.get_client_ip_addr() call fixed independently - Add before/after snippet for handle_publisher_request call site in main.rs - Add noop_services import instruction for http_util.rs test module - Clarify _services rename (drop underscore, not add new param) in didomi.rs - Clarify nextjs #[allow(deprecated)] annotations are out of scope (different function) * Update PR7 spec to address all five agent review findings - Change RequestInfo::from_request signature to &ClientInfo (not &RuntimeServices) so prebid can call it with context.client_info - Scope SDK-call acceptance criteria to active non-deprecated code only - List all six AuctionContext construction sites including two production sites in orchestrator.rs and three test helpers in orchestrator/prebid - Add explicit warn-and-continue pattern for publisher.rs geo lookup - Correct testing table: formats.rs and endpoints.rs have no test modules; add orchestrator.rs and prebid.rs test helper update rows * Add PR7 implementation plan and address plan review findings Plan covers 6 tasks in compilation-safe order: AuctionContext struct change first, then from_request signature, then synthetic.rs cascade, then publisher geo, then didomi. Includes two new copy_headers unit tests (Some/None). Spec fixes: clarify injection pattern exceptions for &ClientInfo and Option<IpAddr>; reword acceptance criterion to reflect that provider-layer reads flow through AuctionContext.client_info. * Fix three plan review findings and two open questions - Finding 1 (High): Add missing publisher.rs test call site at line ~695 for get_or_generate_synthetic_id — was omitted from Task 3 Step 6 - Finding 2 (Medium): Remove crate::geo::GeoInfo import from endpoints.rs rather than replacing it — type is not used by name after the change, keeping any import fails clippy -D warnings - Finding 3 (Low): Replace interactive git add -p in Task 6 with explicit file staging instruction - Open Q1: Add Task 2 step to update stale handle_publisher_request signature in auction/README.md - Open Q2: Add Task 2 step to update from_request doc comment to reflect ClientInfo-based TLS detection instead of Fastly SDK calls * Broaden two low-severity doc cleanup steps in PR7 plan - Step 7: cover all four stale Fastly-SDK-specific locations in http_util.rs (SPOOFABLE_FORWARDED_HEADERS doc, RequestInfo struct doc, from_request doc, detect_request_scheme doc) - Step 8: replace the whole routing snippet in auction/README.md, not just the one handle_publisher_request line — handle_auction and integration_registry.handle_proxy are also stale in that snippet * Fix two remaining low findings in PR7 plan - Add missing Location 2 (RequestInfo.scheme field doc, line ~67) to Step 7; renumber subsequent locations 3-5 - Replace &runtime_services with runtime_services in Step 5 and README snippet — runtime_services is already &RuntimeServices in route_request * Fix count drift in Step 7: four → five locations * Add client_info field to AuctionContext and fix all construction sites * Change RequestInfo::from_request to take &ClientInfo, thread services into handle_publisher_request * Add Task 2 follow-up coverage and README route fixes * Add services param to generate_synthetic_id, remove Fastly IP/geo calls in formats and endpoints * Revert premature publisher geo change from Task 3 * Replace deprecated GeoInfo::from_request in publisher.rs with services.geo().lookup() * Remove Fastly IP extraction from Didomi copy_headers, use ClientInfo instead * Move IpAddr import to test module level in didomi.rs * Apply rustfmt formatting to didomi.rs, publisher.rs, and synthetic.rs Fix multi-line function call style in didomi.rs, line-break wrapping in publisher.rs test, and import ordering in synthetic.rs test module. * Add test coverage for generate_synthetic_id with concrete client IP Adds noop_services_with_client_ip helper to test_support and a new test that verifies the client_ip path through generate_synthetic_id by asserting the HMAC differs when the IP changes. * Align geo lookup warn log format with codebase convention ({e} not {e:?}) * Apply Prettier formatting to PR7 plan and spec docs * Document content rewriting as platform-agnostic in platform module * Document html_processor as platform-agnostic * Document streaming_processor as platform-agnostic * Fix unresolved doc link: replace EdgeRequest with edgezero_core::http::Request * Add plan for content rewriting * Add plan for PR9: wire signing to store primitives * Add build_services_with_config_and_secret to test_support * Add FastlyManagementApiClient to adapter * Implement FastlyPlatformConfigStore and FastlyPlatformSecretStore write methods via management API Replace FastlyApiClient with FastlyManagementApiClient in the put/delete methods of FastlyPlatformConfigStore and the create/delete methods of FastlyPlatformSecretStore. Remove the now-unused FastlyApiClient import. * Add services to AuctionContext; remove deprecated from_config shim Thread RuntimeServices into AuctionContext so auction providers can access platform stores directly. Update PrebidAuctionProvider to use RequestSigner::from_services(context.services) instead of the now- removed from_config() shim. All construction sites and test helpers updated accordingly. This satisfies the final acceptance criterion of #490: no FastlyConfigStore/FastlySecretStore construction remains in the request_signing/ modules. * Fix rotate/delete atomicity, HTTP verb, idempotent deletes, and weak tests - Revert proxy.rs merge artifact: restore per-request allowed_domains at both redirect_is_permitted call sites; remove dead_code allow and stale comment — integration proxies defaulting to &[] get open mode again as documented - Drop unused trusted-server-js dep from adapter Cargo.toml - Fix check_response: gate body read behind error branch so 2xx paths do not buffer and discard the response body - Remove self-referential SECRET_UPSERT_METHOD test - Reorder write-cost doc so outbound HTTPS round-trip leads; handle-open caching noted as negligible - Refactor make_request to take fastly::http::Method; drop string match and unreachable arm; remove SECRET_UPSERT_METHOD const - Add SigningStoreIds named struct in endpoints.rs; update both call sites to destructure by name * Move logging initialization into Fastly adapter (PR 10) (#610) Move logging initialization into Fastly adapter (PR 10) (#610) - Reverted gratuitous _message rename and record.args() usage in logging.rs, returning to the idiomatic message parameter inside the fern format closure. - Refactored target_label to use .rsplit_once("::") rather than .split("::").last(). This provides a more explicit and robust way to extract the final module segment. - Expanded target_label test coverage to explicitly test edge cases such as inputs without :: separators, empty strings, and inputs with trailing ::.
8643550 to
24d4515
Compare
Summary
_sp_user_consent_*from localStorage and mirrors GPP consent into__gpp/__gpp_sidcookiessale_opt_outfrom US GPP sections (IDs 7–23)allows_ec_creation()between existing TCF andus_privacychecksCloses #640
Test plan
cargo test --workspace— 992 tests including 8 new)npx vitest run— 288 tests including 6 new)🤖 Generated with Claude Code