Skip to content

Commit 20b80ba

Browse files
Merge latest PR #992 changes into rc/july
2 parents ca6398d + c82c640 commit 20b80ba

10 files changed

Lines changed: 1378 additions & 36 deletions

File tree

‎crates/trusted-server-core/benches/html_processor_bench.rs‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ fn make_config() -> HtmlProcessorConfig {
1313
ad_bids_state: std::sync::Arc::new(std::sync::Mutex::new(None)),
1414
max_buffered_body_bytes: 16 * 1024 * 1024,
1515
gpt_diagnostics: None,
16+
suppress_datadome_client_side_tag: false,
1617
}
1718
}
1819

‎crates/trusted-server-core/src/html_processor.rs‎

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ use lol_html::{
1313
text,
1414
};
1515

16+
use crate::integrations::datadome::{DATADOME_INTEGRATION_ID, DataDomeClientTagSuppressed};
1617
use crate::integrations::gpt_diagnostics::GptDiagnosticsRequestDecision;
1718
use crate::integrations::{
1819
AttributeRewriteOutcome, IntegrationAttributeContext, IntegrationDocumentState,
@@ -175,6 +176,8 @@ pub struct HtmlProcessorConfig {
175176
pub max_buffered_body_bytes: usize,
176177
/// Request-scoped conditional diagnostics delivery decision.
177178
pub gpt_diagnostics: Option<GptDiagnosticsRequestDecision>,
179+
/// Whether to omit Trusted Server's automatic `DataDome` client-side tag.
180+
pub suppress_datadome_client_side_tag: bool,
178181
}
179182

180183
impl HtmlProcessorConfig {
@@ -196,6 +199,7 @@ impl HtmlProcessorConfig {
196199
ad_bids_state: std::sync::Arc::new(std::sync::Mutex::new(None)),
197200
max_buffered_body_bytes: settings.publisher.max_buffered_body_bytes,
198201
gpt_diagnostics: None,
202+
suppress_datadome_client_side_tag: false,
199203
}
200204
}
201205

@@ -223,6 +227,13 @@ impl HtmlProcessorConfig {
223227
self.gpt_diagnostics = decision;
224228
self
225229
}
230+
231+
/// Attach the request-scoped `DataDome` client-tag suppression decision.
232+
#[must_use]
233+
pub fn with_datadome_client_tag_suppression(mut self, suppress: bool) -> Self {
234+
self.suppress_datadome_client_side_tag = suppress;
235+
self
236+
}
226237
}
227238

228239
/// Create an HTML processor with URL replacement and integration hooks.
@@ -235,6 +246,9 @@ impl HtmlProcessorConfig {
235246
pub fn create_html_processor(config: HtmlProcessorConfig) -> impl StreamProcessor {
236247
let post_processors = config.integrations.html_post_processors();
237248
let document_state = IntegrationDocumentState::default();
249+
if config.suppress_datadome_client_side_tag {
250+
document_state.get_or_insert_with(DATADOME_INTEGRATION_ID, || DataDomeClientTagSuppressed);
251+
}
238252

239253
// Simplified URL patterns structure - stores only core data and generates variants on-demand
240254
struct UrlPatterns {
@@ -692,6 +706,7 @@ mod tests {
692706
ad_bids_state: std::sync::Arc::new(std::sync::Mutex::new(None)),
693707
max_buffered_body_bytes: 16 * 1024 * 1024,
694708
gpt_diagnostics: None,
709+
suppress_datadome_client_side_tag: false,
695710
}
696711
}
697712

@@ -950,6 +965,46 @@ mod tests {
950965
assert_eq!(config.request_scheme, "https");
951966
}
952967

968+
#[test]
969+
fn suppressed_datadome_tag_is_not_injected_into_processed_html() {
970+
let mut settings = create_test_settings();
971+
settings
972+
.integrations
973+
.insert_config(
974+
"datadome",
975+
&json!({
976+
"enabled": true,
977+
"client_side_key": "test-client-key",
978+
}),
979+
)
980+
.expect("should configure DataDome integration");
981+
let registry = IntegrationRegistry::new(&settings)
982+
.expect("should create integration registry with DataDome");
983+
let config = HtmlProcessorConfig::from_settings(
984+
&settings,
985+
&registry,
986+
"origin.example.com",
987+
"test.example.com",
988+
"https",
989+
)
990+
.with_datadome_client_tag_suppression(true);
991+
let mut processor = create_html_processor(config);
992+
993+
let output = processor
994+
.process_chunk(b"<html><head></head><body>content</body></html>", true)
995+
.expect("should process HTML");
996+
let html = String::from_utf8(output).expect("should produce UTF-8 HTML");
997+
998+
assert!(
999+
!html.contains("window.ddjskey"),
1000+
"should omit the DataDome client configuration"
1001+
);
1002+
assert!(
1003+
!html.contains("/integrations/datadome/tags.js"),
1004+
"should omit the DataDome client tag URL"
1005+
);
1006+
}
1007+
9531008
#[test]
9541009
fn test_real_publisher_html() {
9551010
// Test with publisher HTML from test_publisher.html
@@ -1539,6 +1594,7 @@ mod tests {
15391594
ad_bids_state: std::sync::Arc::new(std::sync::Mutex::new(None)),
15401595
max_buffered_body_bytes: 16 * 1024 * 1024,
15411596
gpt_diagnostics: None,
1597+
suppress_datadome_client_side_tag: false,
15421598
};
15431599
let mut processor = create_html_processor(config);
15441600
let output = processor
@@ -1613,6 +1669,7 @@ mod tests {
16131669
ad_bids_state: state,
16141670
max_buffered_body_bytes: 16 * 1024 * 1024,
16151671
gpt_diagnostics: None,
1672+
suppress_datadome_client_side_tag: false,
16161673
};
16171674
let mut processor = create_html_processor(config);
16181675
let output = processor
@@ -1649,6 +1706,7 @@ mod tests {
16491706
ad_bids_state: state,
16501707
max_buffered_body_bytes: 16 * 1024 * 1024,
16511708
gpt_diagnostics: None,
1709+
suppress_datadome_client_side_tag: false,
16521710
};
16531711
let mut processor = create_html_processor(config);
16541712
// Malformed HTML with two <body> elements (common in CMS template pages)
@@ -1684,6 +1742,7 @@ mod tests {
16841742
ad_bids_state: std::sync::Arc::new(std::sync::Mutex::new(None)),
16851743
max_buffered_body_bytes: 16 * 1024 * 1024,
16861744
gpt_diagnostics: None,
1745+
suppress_datadome_client_side_tag: false,
16871746
};
16881747
let mut processor = create_html_processor(config);
16891748
let output = processor
@@ -1737,6 +1796,7 @@ mod tests {
17371796
ad_bids_state: state,
17381797
max_buffered_body_bytes: 16 * 1024 * 1024,
17391798
gpt_diagnostics: None,
1799+
suppress_datadome_client_side_tag: false,
17401800
};
17411801
let mut processor = create_html_processor(config);
17421802
let output = processor
@@ -1764,6 +1824,7 @@ mod tests {
17641824
ad_bids_state: state,
17651825
max_buffered_body_bytes: 16 * 1024 * 1024,
17661826
gpt_diagnostics: None,
1827+
suppress_datadome_client_side_tag: false,
17671828
};
17681829
let mut processor = create_html_processor(config);
17691830
let output = processor

‎crates/trusted-server-core/src/integrations/datadome.rs‎

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,12 @@ pub use protection_scope::{
8888

8989
use protection_scope::ProtectionScope;
9090

91-
pub(super) const DATADOME_INTEGRATION_ID: &str = "datadome";
91+
pub(crate) const DATADOME_INTEGRATION_ID: &str = "datadome";
92+
93+
/// Request marker indicating that Trusted Server should omit its automatic
94+
/// `DataDome` client-side tag for the current response.
95+
#[derive(Debug, Clone, Copy)]
96+
pub(crate) struct DataDomeClientTagSuppressed;
9297

9398
/// Regex pattern for matching and rewriting `DataDome` URLs in script content.
9499
///
@@ -765,7 +770,15 @@ impl IntegrationHeadInjector for DataDomeIntegration {
765770
DATADOME_INTEGRATION_ID
766771
}
767772

768-
fn head_inserts(&self, _ctx: &IntegrationHtmlContext<'_>) -> Vec<String> {
773+
fn head_inserts(&self, ctx: &IntegrationHtmlContext<'_>) -> Vec<String> {
774+
if ctx
775+
.document_state
776+
.get::<DataDomeClientTagSuppressed>(DATADOME_INTEGRATION_ID)
777+
.is_some()
778+
{
779+
return Vec::new();
780+
}
781+
769782
if !self.config.inject_client_side_tag || self.config.client_side_key.trim().is_empty() {
770783
return Vec::new();
771784
}
@@ -1249,6 +1262,20 @@ mod tests {
12491262

12501263
#[test]
12511264
fn head_injector_omits_client_side_tag_when_disabled_or_blank() {
1265+
let mut suppressed = test_config();
1266+
suppressed.client_side_key = "test-client-key".to_string();
1267+
let suppressed_integration = DataDomeIntegration::new(suppressed);
1268+
let suppressed_state = crate::integrations::IntegrationDocumentState::default();
1269+
suppressed_state
1270+
.get_or_insert_with(DATADOME_INTEGRATION_ID, || DataDomeClientTagSuppressed);
1271+
let suppressed_ctx = html_context_for_tests(&suppressed_state);
1272+
assert!(
1273+
suppressed_integration
1274+
.head_inserts(&suppressed_ctx)
1275+
.is_empty(),
1276+
"should omit the tag when the request is IP-excluded"
1277+
);
1278+
12521279
let mut blank_key = test_config();
12531280
blank_key.client_side_key = " ".to_string();
12541281
let integration = DataDomeIntegration::new(blank_key);

0 commit comments

Comments
 (0)