From 706469b294be1e806b1b42a5e7d6a90c51db168f Mon Sep 17 00:00:00 2001 From: Christian Date: Tue, 10 Nov 2026 13:04:42 -0600 Subject: [PATCH 1/4] Allow explicitly opted-in HTTP auction provider endpoints --- .../tests/routes.rs | 1 + .../src/app.rs | 3 + .../trusted-server-adapter-fastly/src/app.rs | 1 + crates/trusted-server-adapter-spin/src/app.rs | 2 + .../src/auction/formats.rs | 1 + crates/trusted-server-core/src/auction/mod.rs | 1 + .../src/auction/openrtb.rs | 1 + .../src/auction/openrtb/tests.rs | 1 + .../src/auction/orchestrator.rs | 3 + .../trusted-server-core/src/auction/plan.rs | 95 ++++++++++++++++++- .../src/auction/routing.rs | 1 + .../src/auction_config_types.rs | 1 + crates/trusted-server-core/src/config.rs | 1 + .../src/creative_opportunities.rs | 1 + .../src/integrations/aps.rs | 2 + .../src/integrations/prebid.rs | 3 + docs/guide/auction-orchestration.md | 19 ++-- 17 files changed, 126 insertions(+), 11 deletions(-) diff --git a/crates/trusted-server-adapter-axum/tests/routes.rs b/crates/trusted-server-adapter-axum/tests/routes.rs index b785734968..5bdc36e496 100644 --- a/crates/trusted-server-adapter-axum/tests/routes.rs +++ b/crates/trusted-server-adapter-axum/tests/routes.rs @@ -72,6 +72,7 @@ async fn aps_profile_serves_renderer_through_adapter_fallback() { settings.auction.providers.insert( "aps-main".parse().expect("should parse APS provider ID"), trusted_server_core::auction::ProviderConfig { + allow_insecure_http: false, protocol: "openrtb-2.6".to_string(), profile: "aps".to_string(), endpoint: "https://aps.example/e/pb/bid".to_string(), diff --git a/crates/trusted-server-adapter-cloudflare/src/app.rs b/crates/trusted-server-adapter-cloudflare/src/app.rs index 668b025ccb..c569050e16 100644 --- a/crates/trusted-server-adapter-cloudflare/src/app.rs +++ b/crates/trusted-server-adapter-cloudflare/src/app.rs @@ -847,6 +847,7 @@ mod tests { settings.auction.providers.insert( "aps-main".parse().expect("should parse APS provider ID"), trusted_server_core::auction::ProviderConfig { + allow_insecure_http: false, protocol: "openrtb-2.6".to_string(), profile: "aps".to_string(), endpoint: "https://aps.example/e/pb/bid".to_string(), @@ -913,6 +914,7 @@ mod tests { ( id.parse().expect("should parse provider ID"), trusted_server_core::auction::ProviderConfig { + allow_insecure_http: false, protocol: "openrtb-2.6".to_string(), profile: "standard".to_string(), endpoint: format!("https://{id}.example/openrtb"), @@ -957,6 +959,7 @@ mod tests { ( id.parse().expect("should parse provider ID"), trusted_server_core::auction::ProviderConfig { + allow_insecure_http: false, protocol: "openrtb-2.6".to_string(), profile: "standard".to_string(), endpoint: format!("https://{id}.example/openrtb"), diff --git a/crates/trusted-server-adapter-fastly/src/app.rs b/crates/trusted-server-adapter-fastly/src/app.rs index 69675e2669..e7f19055dd 100644 --- a/crates/trusted-server-adapter-fastly/src/app.rs +++ b/crates/trusted-server-adapter-fastly/src/app.rs @@ -1829,6 +1829,7 @@ mod tests { settings.auction.providers.insert( "aps-main".parse().expect("should parse APS provider ID"), trusted_server_core::auction::ProviderConfig { + allow_insecure_http: false, protocol: "openrtb-2.6".to_string(), profile: "aps".to_string(), endpoint: "https://aps.example/e/pb/bid".to_string(), diff --git a/crates/trusted-server-adapter-spin/src/app.rs b/crates/trusted-server-adapter-spin/src/app.rs index 9edce69fa3..edf956da2d 100644 --- a/crates/trusted-server-adapter-spin/src/app.rs +++ b/crates/trusted-server-adapter-spin/src/app.rs @@ -1038,6 +1038,7 @@ mod tests { ( id.parse().expect("should parse provider ID"), trusted_server_core::auction::ProviderConfig { + allow_insecure_http: false, protocol: "openrtb-2.6".to_string(), profile: "standard".to_string(), endpoint: format!("https://{id}.example/openrtb"), @@ -1062,6 +1063,7 @@ mod tests { settings.auction.providers.insert( "aps-main".parse().expect("should parse APS provider ID"), trusted_server_core::auction::ProviderConfig { + allow_insecure_http: false, protocol: "openrtb-2.6".to_string(), profile: "aps".to_string(), endpoint: "https://aps.example/e/pb/bid".to_string(), diff --git a/crates/trusted-server-core/src/auction/formats.rs b/crates/trusted-server-core/src/auction/formats.rs index 0c49c1e534..576bde7719 100644 --- a/crates/trusted-server-core/src/auction/formats.rs +++ b/crates/trusted-server-core/src/auction/formats.rs @@ -625,6 +625,7 @@ mod tests { providers: BTreeMap::from([( ProviderId::from_str("pbs-primary").expect("should parse provider ID"), ProviderConfig { + allow_insecure_http: false, protocol: "openrtb-2.6".to_string(), profile: "prebid-server".to_string(), endpoint: "https://pbs.example.test/openrtb".to_string(), diff --git a/crates/trusted-server-core/src/auction/mod.rs b/crates/trusted-server-core/src/auction/mod.rs index 4323039284..7855e9d46e 100644 --- a/crates/trusted-server-core/src/auction/mod.rs +++ b/crates/trusted-server-core/src/auction/mod.rs @@ -215,6 +215,7 @@ mod plan_sharing_tests { settings.auction.providers = std::collections::BTreeMap::from([( "aps-main".parse().expect("should parse APS provider ID"), ProviderConfig { + allow_insecure_http: false, protocol: "openrtb-2.6".to_string(), profile: "aps".to_string(), endpoint: "https://aps.example/e/pb/bid".to_string(), diff --git a/crates/trusted-server-core/src/auction/openrtb.rs b/crates/trusted-server-core/src/auction/openrtb.rs index 12c885d88e..280c87501c 100644 --- a/crates/trusted-server-core/src/auction/openrtb.rs +++ b/crates/trusted-server-core/src/auction/openrtb.rs @@ -960,6 +960,7 @@ mod routing_metadata_tests { providers: BTreeMap::from([( provider_id.clone(), ProviderConfig { + allow_insecure_http: false, protocol: "openrtb-2.6".to_string(), profile: profile.to_string(), endpoint: if profile == "aps" { diff --git a/crates/trusted-server-core/src/auction/openrtb/tests.rs b/crates/trusted-server-core/src/auction/openrtb/tests.rs index f47efa475e..d64a57286d 100644 --- a/crates/trusted-server-core/src/auction/openrtb/tests.rs +++ b/crates/trusted-server-core/src/auction/openrtb/tests.rs @@ -42,6 +42,7 @@ fn config_with_endpoint(profile: &str, profile_config: Value, endpoint: &str) -> providers: BTreeMap::from([( provider_id.clone(), ProviderConfig { + allow_insecure_http: false, protocol: "openrtb-2.6".to_string(), profile: profile.to_string(), endpoint: endpoint.to_string(), diff --git a/crates/trusted-server-core/src/auction/orchestrator.rs b/crates/trusted-server-core/src/auction/orchestrator.rs index f665441723..e3e4656cf3 100644 --- a/crates/trusted-server-core/src/auction/orchestrator.rs +++ b/crates/trusted-server-core/src/auction/orchestrator.rs @@ -2835,6 +2835,7 @@ mod tests { ( ProviderId::from_str(id).expect("should parse fictional provider ID"), ProviderConfig { + allow_insecure_http: false, protocol: "openrtb-2.6".to_string(), profile: "standard".to_string(), endpoint: "https://example.test/openrtb".to_string(), @@ -2867,6 +2868,7 @@ mod tests { ( ProviderId::from_str(id).expect("should parse fictional provider ID"), ProviderConfig { + allow_insecure_http: false, protocol: "openrtb-2.6".to_string(), profile: "prebid-server".to_string(), endpoint: format!("https://{id}.example.test/openrtb"), @@ -2903,6 +2905,7 @@ mod tests { ( ProviderId::from_str(id).expect("should parse fictional provider ID"), ProviderConfig { + allow_insecure_http: false, protocol: "openrtb-2.6".to_string(), profile: "aps".to_string(), endpoint: "https://aps.example/e/pb/bid".to_string(), diff --git a/crates/trusted-server-core/src/auction/plan.rs b/crates/trusted-server-core/src/auction/plan.rs index df36fc09b7..dad67dc2a6 100644 --- a/crates/trusted-server-core/src/auction/plan.rs +++ b/crates/trusted-server-core/src/auction/plan.rs @@ -135,6 +135,10 @@ pub struct ProviderConfig { pub profile: String, /// Fixed provider endpoint. pub endpoint: String, + /// Permit unencrypted HTTP for this provider only. Defaults to false. + /// Use only for explicitly approved demo/test endpoints. + #[serde(default, skip_serializing_if = "is_false")] + pub allow_insecure_http: bool, /// Optional profile-default timeout override. #[serde(default)] pub timeout_ms: Option, @@ -314,7 +318,12 @@ impl AuctionPlan { "provider `{id}` profile_config must be an object" ))); } - let endpoint = canonicalize_endpoint(&id, registration.id, &raw.endpoint)?; + let endpoint = canonicalize_endpoint( + &id, + registration.id, + &raw.endpoint, + raw.allow_insecure_http, + )?; let timeout_ms = raw .timeout_ms .unwrap_or(match registration.default_timeout { @@ -566,13 +575,14 @@ fn canonicalize_endpoint( provider_id: &ProviderId, profile_id: &str, value: &str, + allow_insecure_http: bool, ) -> Result> { let mut endpoint = Url::parse(value).map_err(|error| { configuration_error(format!( "provider `{provider_id}` endpoint must be an absolute HTTPS URL: {error}" )) })?; - if endpoint.scheme() != "https" + if !(endpoint.scheme() == "https" || (allow_insecure_http && endpoint.scheme() == "http")) || endpoint.host_str().is_none() || !endpoint.username().is_empty() || endpoint.password().is_some() @@ -598,6 +608,10 @@ fn canonicalize_endpoint( Ok(CanonicalProviderEndpoint(endpoint)) } +fn is_false(value: &bool) -> bool { + !*value +} + fn normalize_prebid_server_endpoint(endpoint: &mut Url) { match endpoint.path() { "" | "/" => endpoint.set_path("/openrtb2/auction"), @@ -644,6 +658,7 @@ mod tests { fn provider(profile: &str) -> ProviderConfig { ProviderConfig { + allow_insecure_http: false, protocol: "openrtb-2.6".to_string(), profile: profile.to_string(), endpoint: "https://bid.example/openrtb2/auction".to_string(), @@ -1123,6 +1138,82 @@ mod tests { assert!(AuctionPlan::compile(config(BTreeMap::from([(id("aps"), aps)]))).is_err()); } + #[test] + fn insecure_http_requires_an_explicit_per_provider_opt_in() { + let mut demo = provider("prebid-server"); + demo.endpoint = "http://192.0.2.1:8000".to_string(); + assert!( + AuctionPlan::compile(config(BTreeMap::from([(id("demo"), demo.clone())]))).is_err(), + "should reject HTTP without the provider's explicit opt-in" + ); + demo.allow_insecure_http = true; + let plan = AuctionPlan::compile(config(BTreeMap::from([(id("demo"), demo.clone())]))) + .expect("should compile an explicitly opted-in HTTP provider"); + assert_eq!( + plan.providers()[0].endpoint.as_str(), + "http://192.0.2.1:8000/openrtb2/auction", + "should normalize the opted-in Prebid Server endpoint" + ); + let mut other = provider("standard"); + other.endpoint = "http://other.example.com/bid".to_string(); + assert!( + AuctionPlan::compile(config(BTreeMap::from([ + (id("demo"), demo), + (id("other"), other) + ]))) + .is_err(), + "should not inherit another provider's HTTP opt-in" + ); + } + + #[test] + fn insecure_http_opt_in_does_not_allow_credentials_fragments_or_other_schemes() { + for endpoint in [ + "http://user:pass@pbs.example.com/bid", + "http://pbs.example.com/bid#fragment", + "ftp://pbs.example.com/bid", + "/relative", + ] { + let mut raw = provider("prebid-server"); + raw.allow_insecure_http = true; + raw.endpoint = endpoint.to_string(); + assert!( + AuctionPlan::compile(config(BTreeMap::from([(id("demo"), raw)]))).is_err(), + "should reject {endpoint}" + ); + } + } + + #[test] + fn insecure_http_default_preserves_existing_provider_wire_shape() { + let legacy = serde_json::to_value(provider("prebid-server")) + .expect("should serialize the default provider"); + assert!( + legacy.get("allow_insecure_http").is_none(), + "should omit the default HTTP opt-in field" + ); + assert!( + !serde_json::from_value::(legacy) + .expect("should deserialize the default provider") + .allow_insecure_http, + "should keep HTTP disabled when the field is absent" + ); + let mut opted_in = provider("prebid-server"); + opted_in.allow_insecure_http = true; + let encoded = + serde_json::to_value(opted_in).expect("should serialize the opted-in provider"); + assert_eq!( + encoded["allow_insecure_http"], true, + "should publish the explicit HTTP opt-in" + ); + assert!( + serde_json::from_value::(encoded) + .expect("should deserialize the opted-in provider") + .allow_insecure_http, + "should retain the explicit HTTP opt-in across serialization" + ); + } + #[test] fn compiler_normalizes_only_prebid_server_origin_and_canonical_paths() { for (configured, expected) in [ diff --git a/crates/trusted-server-core/src/auction/routing.rs b/crates/trusted-server-core/src/auction/routing.rs index 4134ae6a7f..1ca8297d20 100644 --- a/crates/trusted-server-core/src/auction/routing.rs +++ b/crates/trusted-server-core/src/auction/routing.rs @@ -579,6 +579,7 @@ mod tests { fn provider(profile: &str, routing: RoutingMode) -> ProviderConfig { ProviderConfig { + allow_insecure_http: false, protocol: "openrtb-2.6".to_string(), profile: profile.to_string(), endpoint: format!("https://{profile}.example.test/openrtb"), diff --git a/crates/trusted-server-core/src/auction_config_types.rs b/crates/trusted-server-core/src/auction_config_types.rs index 7f506b89bf..36b85dbd95 100644 --- a/crates/trusted-server-core/src/auction_config_types.rs +++ b/crates/trusted-server-core/src/auction_config_types.rs @@ -207,6 +207,7 @@ impl AuctionConfig { ( id, ProviderConfig { + allow_insecure_http: false, protocol: "openrtb-2.6".to_string(), profile: "standard".to_string(), endpoint: format!("https://{name}.example/openrtb2/auction"), diff --git a/crates/trusted-server-core/src/config.rs b/crates/trusted-server-core/src/config.rs index 141a06867e..732749686c 100644 --- a/crates/trusted-server-core/src/config.rs +++ b/crates/trusted-server-core/src/config.rs @@ -911,6 +911,7 @@ formats = [{ width = 300, height = 250 }] settings.auction.providers.insert( "aps-main".parse().expect("should parse APS provider ID"), ProviderConfig { + allow_insecure_http: false, protocol: "openrtb-2.6".to_string(), profile: "aps".to_string(), endpoint: "https://aps.example.com/e/pb/bid".to_string(), diff --git a/crates/trusted-server-core/src/creative_opportunities.rs b/crates/trusted-server-core/src/creative_opportunities.rs index 8a20c72965..75c893d4a9 100644 --- a/crates/trusted-server-core/src/creative_opportunities.rs +++ b/crates/trusted-server-core/src/creative_opportunities.rs @@ -2383,6 +2383,7 @@ mod tests { providers: BTreeMap::from([( ProviderId::from_str("pbs-primary").expect("should parse provider ID"), ProviderConfig { + allow_insecure_http: false, protocol: "openrtb-2.6".to_string(), profile: "prebid-server".to_string(), endpoint: "https://pbs.example.test/openrtb".to_string(), diff --git a/crates/trusted-server-core/src/integrations/aps.rs b/crates/trusted-server-core/src/integrations/aps.rs index 62f8c8f068..08469c4d74 100644 --- a/crates/trusted-server-core/src/integrations/aps.rs +++ b/crates/trusted-server-core/src/integrations/aps.rs @@ -1939,6 +1939,7 @@ pub fn register( providers: BTreeMap::from([( "aps".parse().expect("should parse APS provider ID"), ProviderConfig { + allow_insecure_http: false, protocol: "openrtb-2.6".to_string(), profile: "aps".to_string(), endpoint: default_endpoint(), @@ -3077,6 +3078,7 @@ mod tests { providers: BTreeMap::from([( "aps".parse().expect("should parse APS provider ID"), ProviderConfig { + allow_insecure_http: false, protocol: "openrtb-2.6".to_string(), profile: "aps".to_string(), endpoint: default_endpoint(), diff --git a/crates/trusted-server-core/src/integrations/prebid.rs b/crates/trusted-server-core/src/integrations/prebid.rs index c13260704f..817f7ec859 100644 --- a/crates/trusted-server-core/src/integrations/prebid.rs +++ b/crates/trusted-server-core/src/integrations/prebid.rs @@ -5622,6 +5622,7 @@ external_bundle_sri = "sha384-AAAA" ( ProviderId::from_str("pbs-primary").expect("should parse provider ID"), ProviderConfig { + allow_insecure_http: false, protocol: "openrtb-2.6".to_string(), profile: "prebid-server".to_string(), endpoint: "https://primary.example.test/openrtb".to_string(), @@ -5634,6 +5635,7 @@ external_bundle_sri = "sha384-AAAA" ( ProviderId::from_str("pbs-secondary").expect("should parse provider ID"), ProviderConfig { + allow_insecure_http: false, protocol: "openrtb-2.6".to_string(), profile: "prebid-server".to_string(), endpoint: "https://secondary.example.test/openrtb".to_string(), @@ -9098,6 +9100,7 @@ set = { networkId = 42 } providers: BTreeMap::from([( provider_id.clone(), ProviderConfig { + allow_insecure_http: false, protocol: "openrtb-2.6".to_string(), profile: "prebid-server".to_string(), endpoint: "https://pbs.example/openrtb2/auction".to_string(), diff --git a/docs/guide/auction-orchestration.md b/docs/guide/auction-orchestration.md index 094212dc4d..298c3499f5 100644 --- a/docs/guide/auction-orchestration.md +++ b/docs/guide/auction-orchestration.md @@ -773,15 +773,16 @@ registered integration selected by `[auction].mediator`. Common provider fields and defaults: -| Field | Default | Meaning | -| ---------------- | --------------- | -------------------------------------------------------------- | -| `protocol` | Required | `openrtb-2.6` | -| `profile` | `standard` | Typed OpenRTB behavior | -| `endpoint` | Required | Fixed absolute HTTPS endpoint | -| `timeout_ms` | Profile default | PBS 1000 ms, APS 800 ms, standard inherits auction timeout | -| `routing` | `explicit` | `explicit`, or `all_eligible` for non-PBS profiles | -| `profile_config` | `{}` | Profile-owned typed settings | -| `notifications` | No suppression | Common `nurl`/`burl` suppression by all bids or returned seats | +| Field | Default | Meaning | +| --------------------- | --------------- | ---------------------------------------------------------------------------------- | +| `protocol` | Required | `openrtb-2.6` | +| `profile` | `standard` | Typed OpenRTB behavior | +| `endpoint` | Required | Fixed absolute HTTPS endpoint | +| `allow_insecure_http` | `false` | Explicit per-provider HTTP opt-in for approved demos/tests; traffic is unencrypted | +| `timeout_ms` | Profile default | PBS 1000 ms, APS 800 ms, standard inherits auction timeout | +| `routing` | `explicit` | `explicit`, or `all_eligible` for non-PBS profiles | +| `profile_config` | `{}` | Profile-owned typed settings | +| `notifications` | No suppression | Common `nurl`/`burl` suppression by all bids or returned seats | APS normally uses `all_eligible`, which sends every compatible banner slot but never another provider's bidder parameters. `explicit` providers receive only From 925aaed65c5cfe13a40fa405599acaa435de3033 Mon Sep 17 00:00:00 2001 From: Christian Date: Tue, 10 Nov 2026 15:34:02 -0600 Subject: [PATCH 2/4] Document provider-specific HTTP auction opt-in design --- ...-10-http-auction-provider-opt-in-design.md | 61 +++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 docs/superpowers/specs/2026-11-10-http-auction-provider-opt-in-design.md diff --git a/docs/superpowers/specs/2026-11-10-http-auction-provider-opt-in-design.md b/docs/superpowers/specs/2026-11-10-http-auction-provider-opt-in-design.md new file mode 100644 index 0000000000..f221e809bc --- /dev/null +++ b/docs/superpowers/specs/2026-11-10-http-auction-provider-opt-in-design.md @@ -0,0 +1,61 @@ +# Provider-specific HTTP auction opt-in + +## Intent + +Permit an explicitly configured auction provider to use unencrypted HTTP for an approved demo or test endpoint. Keep HTTPS as the default. Do not turn one provider's exception into permission for another provider. + +This documents the existing reconstructed feature at `2c79ea59`. Its formal design reviews occur before publication readiness, not before the earlier implementation. It does not authorize deployment or production use of unencrypted auction traffic. + +## Configuration contract + +`ProviderConfig.allow_insecure_http` is a boolean, defaults to `false` when absent and is omitted during serialization while false. An explicit true value survives serialization. Existing provider documents therefore keep their default wire shape. + +For one provider: + +| Parsed endpoint | Flag false or absent | Flag true | +| ------------------------------------------- | ------------------------------------- | ------------------------------------- | +| Absolute HTTPS with host | Accept subject to existing validation | Accept subject to existing validation | +| Absolute HTTP with host | Reject | Accept subject to existing validation | +| Username/password, fragment or missing host | Reject | Reject | +| Relative URL or another scheme | Reject | Reject | + +The flag is an operator-selected permission, not proof that an endpoint is approved, isolated or safe. HTTP exposes request and response data on the network. It adds no DNS/private-network allowlist and cannot make an insecure connection confidential. + +Example, with fictional inputs: + +```toml +[auction.providers.demo] +protocol = "openrtb-2.6" +profile = "prebid-server" +endpoint = "http://bids.example.com:8000" +allow_insecure_http = true +``` + +## Boundaries + +Auction-plan compilation owns this validation. Read the flag from the same provider whose endpoint is being compiled. Do not use a global toggle, profile-wide mutable state, another provider's flag or an environment fallback. + +Keep existing profile validation and endpoint normalization. In particular, Prebid Server origin/canonical-path normalization still chooses `/openrtb2/auction`; other existing profile restrictions remain authoritative. HTTPS is not downgraded when the flag is true. + +Transport selection, redirect policy, timeout/cutoff handling, consent, bidder eligibility, request signing, caching and response limits remain unchanged. Accepting a configured URL does not promise that every runtime can reach it or override a runtime's network restrictions. + +Existing runtime/test provider literals explicitly select false unless that test is about the opt-in. No credentials or real deployment endpoints belong in examples or fixtures. + +## Required proof + +- HTTP fails without the flag and succeeds with that provider's explicit true value. +- A second HTTP provider without the flag still fails, even beside an opted-in provider. +- True does not allow credentials, fragments, relative URLs or another scheme. +- Default serialization omits the flag, absent deserialization returns false and true round-trips. +- Existing HTTPS/profile normalization cases continue passing. +- All affected adapter consumers compile and pass their target-matched checks in the final stack. A native-only plan test is not hosted/network qualification. + +The focused existing command is `cargo test --locked -p trusted-server-core auction::plan::tests`. Application and adapter checks follow the repository CI gate list. No cloud request or deployment is needed to establish the configuration contract. + +## Non-goals + +No global HTTP policy, automatic TLS fallback, alternate transport, credentials-in-URL support, new provider profile, network allowlist, production security approval or deployment workflow. + +## Related + +- [Auction orchestration guide](../../guide/auction-orchestration.md) From dcac90e882c0bbdc4d25ea5311640353c2d96d66 Mon Sep 17 00:00:00 2001 From: Christian Date: Tue, 10 Nov 2026 18:15:24 -0600 Subject: [PATCH 3/4] Record completed HTTP opt-in design reviews --- .../2026-11-10-http-auction-provider-opt-in-design.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/docs/superpowers/specs/2026-11-10-http-auction-provider-opt-in-design.md b/docs/superpowers/specs/2026-11-10-http-auction-provider-opt-in-design.md index f221e809bc..9f27fd9d5c 100644 --- a/docs/superpowers/specs/2026-11-10-http-auction-provider-opt-in-design.md +++ b/docs/superpowers/specs/2026-11-10-http-auction-provider-opt-in-design.md @@ -56,6 +56,17 @@ The focused existing command is `cargo test --locked -p trusted-server-core auct No global HTTP policy, automatic TLS fallback, alternate transport, credentials-in-URL support, new provider profile, network allowlist, production security approval or deployment workflow. +## Review log + +The implementation existed before these independent read-only reviews. They are not retroactive preimplementation approval or deployment authorization. + +| Date | Round | Frozen target | Result | +| ---------- | -------------- | ----------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- | +| 2026-11-10 | 1 | Contract SHA256 `e4ad3f9a8743fd332bd12b239cab94a6e6aa538428b6404fc0d14be5635c186e`; implementation `dd2ca144..2c79ea59` | No blocking or nonblocking findings. Provider-local permission, URL validation, serialization and consumer changes inspected. | +| 2026-11-10 | 2, independent | Same frozen contract and complete 17-file implementation delta | No findings or requested changes. No tests or builds executed by the reviewer. | + +The formatted contract retained the same meaning. This extraction replays the HTTP unit without relay ancestry onto current main; the review log records its original reviewed inputs, not a whole-tree review of later changes. Current target-matched checks remain required before readiness. + ## Related - [Auction orchestration guide](../../guide/auction-orchestration.md) From cd8977cad39d8388d99f6ff3f93083ba880deb3e Mon Sep 17 00:00:00 2001 From: Christian Date: Tue, 10 Nov 2026 18:52:21 -0600 Subject: [PATCH 4/4] Use an example hostname in the HTTP opt-in fixture --- crates/trusted-server-core/src/auction/plan.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/trusted-server-core/src/auction/plan.rs b/crates/trusted-server-core/src/auction/plan.rs index dad67dc2a6..ecc89734c9 100644 --- a/crates/trusted-server-core/src/auction/plan.rs +++ b/crates/trusted-server-core/src/auction/plan.rs @@ -1141,7 +1141,7 @@ mod tests { #[test] fn insecure_http_requires_an_explicit_per_provider_opt_in() { let mut demo = provider("prebid-server"); - demo.endpoint = "http://192.0.2.1:8000".to_string(); + demo.endpoint = "http://pbs.example.com:8000".to_string(); assert!( AuctionPlan::compile(config(BTreeMap::from([(id("demo"), demo.clone())]))).is_err(), "should reject HTTP without the provider's explicit opt-in" @@ -1151,7 +1151,7 @@ mod tests { .expect("should compile an explicitly opted-in HTTP provider"); assert_eq!( plan.providers()[0].endpoint.as_str(), - "http://192.0.2.1:8000/openrtb2/auction", + "http://pbs.example.com:8000/openrtb2/auction", "should normalize the opted-in Prebid Server endpoint" ); let mut other = provider("standard");