Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Changed

- **Breaking** — `bid_param_zone_overrides` inner values must now be JSON objects; previously non-object or empty values (`"header" = "x"`, `"header" = {}`) were accepted and silently produced a dead rule at runtime. They now fail at startup with a configuration error. Operators upgrading should audit their `bid_param_zone_overrides` config for non-object zone entries.
- **Breaking** — Integration configuration strings are no longer globally reinterpreted as JSON scalars. Operators upgrading should audit `[integrations.*]` settings and use native TOML/typed-config booleans and numbers (for example, `enabled = true`, not `enabled = "true"`); quoted numeric and boolean scalars now fail validation instead of silently converting.
Comment thread
ChristianPavilonis marked this conversation as resolved.
- **Breaking** — Sourcepoint browser module inclusion now requires explicit `[integrations.sourcepoint].enabled = true`; operators relying on the previous unconditional Sourcepoint module should enable the integration before upgrading.

### Security
Expand Down
95 changes: 85 additions & 10 deletions crates/trusted-server-core/src/integrations/prebid.rs
Original file line number Diff line number Diff line change
Expand Up @@ -289,11 +289,6 @@ pub struct PrebidIntegrationConfig {
/// param1 = 12345
/// param2 = "value"
/// ```
///
/// Example via environment variable:
/// ```text
/// TRUSTED_SERVER__INTEGRATIONS__PREBID__BID_PARAM_OVERRIDES='{"bidder-name":{"param1":12345,"param2":"value"}}'
/// ```
#[serde(default)]
pub bid_param_overrides: HashMap<String, serde_json::Map<String, Json>>,
Comment thread
ChristianPavilonis marked this conversation as resolved.
/// Canonical ordered bidder-param override rules.
Expand All @@ -311,11 +306,6 @@ pub struct PrebidIntegrationConfig {
/// when.zone = "header"
/// set = { placementId = "_abc" }
/// ```
///
/// Example via environment variable:
/// ```text
/// TRUSTED_SERVER__INTEGRATIONS__PREBID__BID_PARAM_OVERRIDE_RULES='[{"when":{"bidder":"kargo","zone":"header"},"set":{"placementId":"_abc"}}]'
/// ```
#[serde(default)]
pub bid_param_override_rules: Vec<BidParamOverrideRule>,
/// How consent signals are forwarded to Prebid Server.
Expand Down Expand Up @@ -5728,6 +5718,91 @@ set = { keywords = { genre = "news" } }
)
}

#[test]
fn bid_param_override_numeric_strings_survive_runtime_config_roundtrip() {
Comment thread
ChristianPavilonis marked this conversation as resolved.
let toml_str = format!(
r#"{}

[integrations.prebid]
enabled = true
server_url = "https://prebid.example"
bidders = ["pubmatic"]

[integrations.prebid.bid_param_overrides.pubmatic]
publisherId = "12345"
adSlot = "67890"

[integrations.prebid.bid_param_zone_overrides.pubmatic]
header = {{ placementId = "24680" }}

[[integrations.prebid.bid_param_override_rules]]
when = {{ bidder = "pubmatic", zone = "in_content" }}
set = {{ placementId = "13579" }}
"#,
TOML_BASE
);
let settings = Settings::from_toml(&toml_str).expect("should parse TOML settings");

// This mirrors the typed data transition beneath `ts config push`:
// `BlobEnvelope` data is deserialized by `settings_from_config_blob`.
let serialized = serde_json::to_value(&settings).expect("should serialize settings");
let runtime_settings =
Settings::from_json_value(serialized).expect("should parse runtime JSON settings");
let config = runtime_settings
.integration_config::<PrebidIntegrationConfig>(PREBID_INTEGRATION_ID)
.expect("should parse Prebid config")
.expect("should enable Prebid config");

let static_request = make_auction_request(vec![make_ts_slot(
"ad-static-0",
&json!({ "pubmatic": { "keep": "client" } }),
None,
)]);
let static_ortb = call_to_openrtb(config.clone(), &static_request);
let static_params = bidder_params(&static_ortb);
assert_eq!(
static_params["pubmatic"]["publisherId"],
json!("12345"),
"static override publisherId should remain a string"
);
assert_eq!(
static_params["pubmatic"]["adSlot"],
json!("67890"),
"static override adSlot should remain a string"
);
assert_eq!(
static_params["pubmatic"]["keep"],
json!("client"),
"static override should preserve client parameters"
);

let header_request = make_auction_request(vec![make_ts_slot(
"ad-header-0",
&json!({ "pubmatic": {} }),
Some("header"),
)]);
let header_ortb = call_to_openrtb(config.clone(), &header_request);
let header_params = bidder_params(&header_ortb);
assert_eq!(
header_params["pubmatic"]["placementId"],
json!("24680"),
"zone override placementId should remain a string"
);

let in_content_request = make_auction_request(vec![make_ts_slot(
"ad-in-content-0",
&json!({ "pubmatic": {} }),
Some("in_content"),
)]);
let in_content_ortb = call_to_openrtb(config, &in_content_request);
let in_content_params = bidder_params(&in_content_ortb);
assert_eq!(
in_content_params["pubmatic"]["placementId"],
json!("13579"),
"canonical rule placementId should remain a string"
);
}

#[test]
fn zone_override_replaces_placement_id() {
let mut config = base_config();
Expand Down
Loading
Loading