Skip to content

Commit 517f8f5

Browse files
adds script overrides to toml config
1 parent c109ba3 commit 517f8f5

File tree

5 files changed

+167
-0
lines changed

5 files changed

+167
-0
lines changed

crates/common/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ pub mod prebid_proxy;
4141
pub mod proxy;
4242
pub mod publisher;
4343
pub mod request_signing;
44+
pub mod script_overrides;
4445
pub mod settings;
4546
pub mod settings_data;
4647
pub mod streaming_processor;
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
use fastly::http::StatusCode;
2+
use fastly::Response;
3+
4+
use crate::settings::Settings;
5+
6+
/// Handles requests for overridden scripts by returning an empty JavaScript response.
7+
///
8+
/// This is useful for blocking or stubbing specific script files without breaking
9+
/// the page. The response includes appropriate headers for caching and content type.
10+
///
11+
/// # Returns
12+
///
13+
/// Returns an HTTP 200 response with:
14+
/// - Empty body with a comment explaining the override
15+
/// - `Content-Type: application/javascript; charset=utf-8`
16+
/// - Long cache headers for optimal CDN performance
17+
#[allow(unused)]
18+
pub fn handle_script_override(_settings: &Settings) -> Response {
19+
let body = "// Script overridden by Trusted Server\n";
20+
21+
Response::from_status(StatusCode::OK)
22+
.with_header("Content-Type", "application/javascript; charset=utf-8")
23+
.with_header("Cache-Control", "public, max-age=31536000, immutable")
24+
.with_body(body)
25+
}
26+
27+
#[cfg(test)]
28+
mod tests {
29+
use super::*;
30+
use crate::test_support::tests::create_test_settings;
31+
32+
#[test]
33+
fn test_handle_script_override_returns_empty_js() {
34+
let settings = create_test_settings();
35+
let response = handle_script_override(&settings);
36+
37+
assert_eq!(response.get_status(), StatusCode::OK);
38+
39+
let content_type = response
40+
.get_header_str("Content-Type")
41+
.expect("Content-Type header should be present");
42+
assert_eq!(content_type, "application/javascript; charset=utf-8");
43+
44+
let cache_control = response
45+
.get_header_str("Cache-Control")
46+
.expect("Cache-Control header should be present");
47+
assert!(cache_control.contains("max-age=31536000"));
48+
assert!(cache_control.contains("immutable"));
49+
50+
let body = response.into_body_str();
51+
assert!(body.contains("// Script overridden by Trusted Server"));
52+
}
53+
}

crates/common/src/settings.rs

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,14 @@ pub struct Rewrite {
252252
pub exclude_domains: Vec<String>,
253253
}
254254

255+
#[derive(Debug, Default, Deserialize, Serialize, Validate)]
256+
pub struct ScriptOverrides {
257+
/// List of URL paths that should return empty JavaScript responses.
258+
/// Supports exact path matching only.
259+
#[serde(default)]
260+
pub paths: Vec<String>,
261+
}
262+
255263
impl Rewrite {
256264
/// Checks if a URL should be excluded from rewriting based on domain matching
257265
#[allow(dead_code)]
@@ -279,6 +287,14 @@ impl Rewrite {
279287
}
280288
}
281289

290+
impl ScriptOverrides {
291+
/// Checks if a path should be overridden with an empty script
292+
#[allow(dead_code)]
293+
pub fn is_overridden(&self, path: &str) -> bool {
294+
self.paths.iter().any(|p| p == path)
295+
}
296+
}
297+
282298
#[derive(Debug, Default, Deserialize, Serialize, Validate)]
283299
pub struct Handler {
284300
#[validate(length(min = 1), custom(function = validate_path))]
@@ -336,6 +352,9 @@ pub struct Settings {
336352
#[serde(default)]
337353
#[validate(nested)]
338354
pub rewrite: Rewrite,
355+
#[serde(default)]
356+
#[validate(nested)]
357+
pub script_overrides: ScriptOverrides,
339358
}
340359

341360
#[allow(unused)]
@@ -957,4 +976,83 @@ mod tests {
957976
assert!(!rewrite.is_excluded("not a url"));
958977
assert!(!rewrite.is_excluded(""));
959978
}
979+
980+
#[test]
981+
fn test_script_overrides_is_overridden() {
982+
let overrides = ScriptOverrides {
983+
paths: vec![
984+
"/.static/prebid/1.0.8/prebid.min.js".to_string(),
985+
"/js/analytics.js".to_string(),
986+
],
987+
};
988+
989+
// Should match exact paths
990+
assert!(overrides.is_overridden("/.static/prebid/1.0.8/prebid.min.js"));
991+
assert!(overrides.is_overridden("/js/analytics.js"));
992+
993+
// Should not match similar but different paths
994+
assert!(!overrides.is_overridden("/.static/prebid/1.0.9/prebid.min.js"));
995+
assert!(!overrides.is_overridden("/js/analytics.min.js"));
996+
assert!(!overrides.is_overridden("/other/path.js"));
997+
assert!(!overrides.is_overridden(""));
998+
}
999+
1000+
#[test]
1001+
fn test_script_overrides_empty() {
1002+
let overrides = ScriptOverrides { paths: vec![] };
1003+
1004+
assert!(!overrides.is_overridden("/.static/prebid/1.0.8/prebid.min.js"));
1005+
assert!(!overrides.is_overridden("/js/analytics.js"));
1006+
}
1007+
1008+
#[test]
1009+
fn test_script_overrides_from_toml() {
1010+
let toml_str = r#"
1011+
[publisher]
1012+
domain = "test-publisher.com"
1013+
cookie_domain = ".test-publisher.com"
1014+
origin_url = "https://origin.test-publisher.com"
1015+
proxy_secret = "test-secret"
1016+
1017+
[prebid]
1018+
server_url = "https://test-prebid.com/openrtb2/auction"
1019+
1020+
[synthetic]
1021+
counter_store = "test-counter-store"
1022+
opid_store = "test-opid-store"
1023+
secret_key = "test-secret-key"
1024+
template = "{{client_ip}}:{{user_agent}}"
1025+
1026+
[script_overrides]
1027+
paths = [
1028+
"/.static/prebid/1.0.8/prebid.min.js",
1029+
"/js/old-analytics.js"
1030+
]
1031+
"#;
1032+
1033+
let settings = Settings::from_toml(toml_str).expect("should parse TOML");
1034+
1035+
assert_eq!(settings.script_overrides.paths.len(), 2);
1036+
assert!(settings
1037+
.script_overrides
1038+
.is_overridden("/.static/prebid/1.0.8/prebid.min.js"));
1039+
assert!(settings
1040+
.script_overrides
1041+
.is_overridden("/js/old-analytics.js"));
1042+
assert!(!settings
1043+
.script_overrides
1044+
.is_overridden("/js/other-script.js"));
1045+
}
1046+
1047+
#[test]
1048+
fn test_script_overrides_default_empty() {
1049+
let toml_str = crate_test_settings_str();
1050+
let settings = Settings::from_toml(&toml_str).expect("should parse TOML");
1051+
1052+
// When not specified, script_overrides should default to empty
1053+
assert_eq!(settings.script_overrides.paths.len(), 0);
1054+
assert!(!settings
1055+
.script_overrides
1056+
.is_overridden("/.static/prebid/1.0.8/prebid.min.js"));
1057+
}
9601058
}

crates/fastly/src/main.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ use trusted_server_common::publisher::{handle_publisher_request, handle_tsjs_dyn
1515
use trusted_server_common::request_signing::{
1616
handle_deactivate_key, handle_jwks_endpoint, handle_rotate_key, handle_verify_signature,
1717
};
18+
use trusted_server_common::script_overrides::handle_script_override;
1819
use trusted_server_common::settings::Settings;
1920
use trusted_server_common::settings_data::get_settings;
2021

@@ -56,6 +57,12 @@ async fn route_request(
5657
let path = req.get_path().to_string();
5758
let method = req.get_method().clone();
5859

60+
// Check if this path should return an empty script override
61+
if settings.script_overrides.is_overridden(&path) {
62+
log::info!("Returning script override for path: {}", path);
63+
return Ok(handle_script_override(&settings));
64+
}
65+
5966
// Match known routes and handle them
6067
let result = match (method, path.as_str()) {
6168
// Serve the tsjs library

trusted-server.toml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,3 +56,11 @@ rewrite_scripts = true
5656
# exclude_domains = [
5757
# "*.edgecompute.app",
5858
# ]
59+
60+
# Script Override Configuration
61+
# Define paths that should return empty JavaScript responses
62+
# Useful for blocking specific script versions or third-party scripts
63+
[script_overrides]
64+
paths = [
65+
"/.static/prebid/1.0.8/prebid.min.js",
66+
]

0 commit comments

Comments
 (0)