@@ -78,8 +78,15 @@ use crate::platform::RuntimeServices;
7878use crate :: settings:: { IntegrationConfig , Settings } ;
7979
8080mod protection;
81+ mod protection_scope;
8182
82- const DATADOME_INTEGRATION_ID : & str = "datadome" ;
83+ pub use protection_scope:: {
84+ ProtectionExclusionRuleConfig , ProtectionIpCidrSourceConfig , ProtectionMatcherConfig ,
85+ } ;
86+
87+ use protection_scope:: ProtectionScope ;
88+
89+ pub ( super ) const DATADOME_INTEGRATION_ID : & str = "datadome" ;
8390
8491/// Regex pattern for matching and rewriting `DataDome` URLs in script content.
8592///
@@ -154,13 +161,36 @@ pub struct DataDomeConfig {
154161 #[ validate( range( min = 1 , max = 10000 ) ) ]
155162 pub timeout_ms : u32 ,
156163
157- /// Regex for URLs to exclude from Protection API validation.
158- #[ serde( default = "default_url_pattern_exclusion" ) ]
159- pub url_pattern_exclusion : String ,
160-
161- /// Regex for URLs to include in Protection API validation.
162- #[ serde( default ) ]
163- pub url_pattern_inclusion : String ,
164+ /// HTTP methods excluded from Protection API validation.
165+ #[ serde(
166+ default = "default_protection_excluded_methods" ,
167+ deserialize_with = "crate::settings::vec_from_seq_or_map"
168+ ) ]
169+ pub protection_excluded_methods : Vec < String > ,
170+
171+ /// Client autonomous system numbers excluded from Protection API validation.
172+ #[ serde( default , deserialize_with = "crate::settings::vec_from_seq_or_map" ) ]
173+ pub protection_excluded_asns : Vec < u32 > ,
174+
175+ /// Client IP CIDR ranges excluded from Protection API validation.
176+ #[ serde( default , deserialize_with = "crate::settings::vec_from_seq_or_map" ) ]
177+ pub protection_excluded_ip_cidrs : Vec < String > ,
178+
179+ /// Config Store-backed client IP CIDR ranges excluded from Protection API validation.
180+ #[ serde( default , deserialize_with = "crate::settings::vec_from_seq_or_map" ) ]
181+ pub protection_excluded_ip_cidr_sources : Vec < ProtectionIpCidrSourceConfig > ,
182+
183+ /// Cache TTL for Config Store-backed IP CIDR lists, in seconds.
184+ #[ serde( default = "default_protection_ip_list_cache_ttl_seconds" ) ]
185+ #[ validate( range( min = 1 , max = 86400 ) ) ]
186+ pub protection_ip_list_cache_ttl_seconds : u64 ,
187+
188+ /// Structured exclusion rules for Protection API validation.
189+ #[ serde(
190+ default = "default_protection_exclusion_rules" ,
191+ deserialize_with = "crate::settings::vec_from_seq_or_map"
192+ ) ]
193+ pub protection_exclusion_rules : Vec < ProtectionExclusionRuleConfig > ,
164194
165195 /// Reserved flag for future GraphQL payload extraction.
166196 #[ serde( default ) ]
@@ -219,8 +249,27 @@ fn default_timeout_ms() -> u32 {
219249 1500
220250}
221251
222- fn default_url_pattern_exclusion ( ) -> String {
223- r"\.(avi|flv|mka|mkv|mov|mp4|mpeg|mpg|mp3|flac|ogg|ogm|opus|wav|webm|webp|bmp|gif|ico|jpeg|jpg|png|svg|svgz|swf|eot|otf|ttf|woff|woff2|css|less|js|map)$" . to_string ( )
252+ fn default_static_asset_exclusion_pattern ( ) -> String {
253+ r"(?i)\.(avi|flv|mka|mkv|mov|mp4|mpeg|mpg|mp3|flac|ogg|ogm|opus|wav|webm|webp|bmp|gif|ico|jpeg|jpg|png|svg|svgz|swf|eot|otf|ttf|woff|woff2|css|less|js|map)$" . to_string ( )
254+ }
255+
256+ fn default_protection_excluded_methods ( ) -> Vec < String > {
257+ vec ! [ "OPTIONS" . to_string( ) ]
258+ }
259+
260+ fn default_protection_ip_list_cache_ttl_seconds ( ) -> u64 {
261+ 300
262+ }
263+
264+ fn default_protection_exclusion_rules ( ) -> Vec < ProtectionExclusionRuleConfig > {
265+ vec ! [ ProtectionExclusionRuleConfig {
266+ id: "default-static-assets" . to_string( ) ,
267+ enabled: true ,
268+ methods: Vec :: new( ) ,
269+ matcher: ProtectionMatcherConfig :: PathRegex {
270+ patterns: vec![ default_static_asset_exclusion_pattern( ) ] ,
271+ } ,
272+ } ]
224273}
225274
226275fn default_inject_client_side_tag ( ) -> bool {
@@ -248,8 +297,12 @@ impl Default for DataDomeConfig {
248297 server_side_key_secret_name : default_server_side_key_secret_name ( ) ,
249298 protection_api_origin : default_protection_api_origin ( ) ,
250299 timeout_ms : default_timeout_ms ( ) ,
251- url_pattern_exclusion : default_url_pattern_exclusion ( ) ,
252- url_pattern_inclusion : String :: new ( ) ,
300+ protection_excluded_methods : default_protection_excluded_methods ( ) ,
301+ protection_excluded_asns : Vec :: new ( ) ,
302+ protection_excluded_ip_cidrs : Vec :: new ( ) ,
303+ protection_excluded_ip_cidr_sources : Vec :: new ( ) ,
304+ protection_ip_list_cache_ttl_seconds : default_protection_ip_list_cache_ttl_seconds ( ) ,
305+ protection_exclusion_rules : default_protection_exclusion_rules ( ) ,
253306 enable_graphql_support : false ,
254307 client_side_key : String :: new ( ) ,
255308 inject_client_side_tag : default_inject_client_side_tag ( ) ,
@@ -268,8 +321,7 @@ impl IntegrationConfig for DataDomeConfig {
268321/// `DataDome` integration implementation.
269322pub struct DataDomeIntegration {
270323 config : DataDomeConfig ,
271- protection_exclusion : Option < Regex > ,
272- protection_inclusion : Option < Regex > ,
324+ protection_scope : ProtectionScope ,
273325}
274326
275327impl DataDomeIntegration {
@@ -299,15 +351,11 @@ impl DataDomeIntegration {
299351 log:: warn!( "[datadome] enable_graphql_support is reserved and ignored in v1" ) ;
300352 }
301353
302- let protection_exclusion =
303- Self :: compile_optional_regex ( & config. url_pattern_exclusion , "url_pattern_exclusion" ) ?;
304- let protection_inclusion =
305- Self :: compile_optional_regex ( & config. url_pattern_inclusion , "url_pattern_inclusion" ) ?;
354+ let protection_scope = ProtectionScope :: compile ( & config) ?;
306355
307356 Ok ( Arc :: new ( Self {
308357 config,
309- protection_exclusion,
310- protection_inclusion,
358+ protection_scope,
311359 } ) )
312360 }
313361
@@ -343,19 +391,6 @@ impl DataDomeIntegration {
343391 Ok ( ( ) )
344392 }
345393
346- fn compile_optional_regex (
347- pattern : & str ,
348- name : & str ,
349- ) -> Result < Option < Regex > , Report < TrustedServerError > > {
350- if pattern. trim ( ) . is_empty ( ) {
351- return Ok ( None ) ;
352- }
353-
354- Regex :: new ( & format ! ( "(?i:{pattern})" ) )
355- . map ( Some )
356- . map_err ( |err| Report :: new ( Self :: error ( format ! ( "Invalid {name}: {err}" ) ) ) )
357- }
358-
359394 fn error ( message : impl Into < String > ) -> TrustedServerError {
360395 TrustedServerError :: Integration {
361396 integration : DATADOME_INTEGRATION_ID . to_string ( ) ,
0 commit comments