1#[cfg(feature = "kv_store")]
2use crate::modules::kvstore_ext::KVStoreGetExt;
3use crate::modules::pxconstants::{
4 CUSTOM_COOKIE_HEADER, GRAPHQL_BODY_MAX_LENGTH, MCP_ENDPOINT_PATH, WHITELIST_EXT,
5};
6use crate::px_error;
7use crate::pxcontext::{PXContext, PXModuleMode, TokenVersion};
8#[cfg(target_arch = "wasm32")]
9use fastly::secret_store::SecretStore;
10#[cfg(not(feature = "kv_store"))]
11use fastly::ConfigStore;
12#[cfg(feature = "kv_store")]
13use fastly::KVStore;
14use fastly::{Request, Response};
15use regex::Regex;
16use serde::{
17 de::{self, Deserializer, MapAccess, Visitor},
18 ser::{SerializeMap, Serializer},
19 Deserialize, Serialize,
20};
21use std::collections::HashSet;
22use std::fmt;
23use strum::{EnumProperty, IntoEnumIterator};
24use strum_macros::{AsRefStr, Display, EnumIter, EnumProperty as EnumPropertyMacro};
25
26#[derive(Default, Clone)]
28pub struct PXCustomParams {
29 pub custom_param1: String,
30 pub custom_param2: String,
31 pub custom_param3: String,
32 pub custom_param4: String,
33 pub custom_param5: String,
34 pub custom_param6: String,
35 pub custom_param7: String,
36 pub custom_param8: String,
37 pub custom_param9: String,
38 pub custom_param10: String,
39}
40
41pub type PXEnrichCustomParamsFn = fn(req: &Request, conf: &PXConfig, params: &mut PXCustomParams);
43pub type PXIsSensitiveRequestFn = fn(req: &Request, conf: &PXConfig) -> bool;
45pub type PXIsFilteredRequestFn = fn(req: &Request, conf: &PXConfig) -> bool;
47pub type PXIsEnforcedRequestFn = fn(req: &Request, conf: &PXConfig) -> bool;
49pub type PXIsMonitoredRequestFn = fn(req: &Request, conf: &PXConfig) -> bool;
51pub type PXAdditionalActivityHandlerFn = fn(req: &Request, conf: &PXConfig, ctx: &PXContext);
53pub type PXCorsCustomPreflightHandlerFn = fn(req: &Request, conf: &PXConfig) -> Option<Response>;
55pub type PXCorsCustomBlockResponseHeadersFn =
57 fn(req: &Request, conf: &PXConfig) -> Vec<(String, String)>;
58
59#[derive(Debug, Clone, Default, PartialEq, Eq)]
61pub struct PXRawCredentials {
62 pub user: Option<String>,
63 pub pass: Option<String>,
64}
65
66impl PXRawCredentials {
67 pub fn without_empty_fields(mut self) -> Self {
69 if self.user.as_deref().is_some_and(str::is_empty) {
70 self.user = None;
71 }
72 if self.pass.as_deref().is_some_and(str::is_empty) {
73 self.pass = None;
74 }
75 self
76 }
77}
78
79pub type PXExtractCredentialsFn =
81 fn(req: &Request, endpoint_index: usize) -> Option<PXRawCredentials>;
82
83pub type PXLoginSuccessfulFn = fn(resp: &Response, endpoint_index: usize) -> Option<bool>;
85
86#[derive(Debug, Clone, Default, Deserialize, Serialize, PartialEq, Eq)]
88pub struct PXCredentialEndpointConfig {
89 pub path: String,
90 #[serde(default)]
91 pub path_type: String,
92 pub method: String,
93 #[serde(default)]
94 pub sent_through: String,
95 #[serde(default)]
96 pub user_field: String,
97 #[serde(default)]
98 pub pass_field: String,
99 #[serde(default)]
100 pub protocol: String,
101 #[serde(default)]
102 pub login_successful_reporting_method: String,
103 #[serde(default)]
104 pub login_successful_statuses: Vec<u16>,
105 #[serde(default)]
106 pub login_successful_body_regex: String,
107 #[serde(default)]
108 pub login_successful_header_name: String,
109 #[serde(default)]
110 pub login_successful_header_value: String,
111}
112
113#[derive(Clone)]
115pub struct PXPreparedCredentialEndpoint {
116 pub config: PXCredentialEndpointConfig,
117 pub path_regex: Option<Regex>,
118}
119
120#[derive(
133 Display, AsRefStr, EnumIter, EnumPropertyMacro, Clone, Copy, PartialEq, Eq, Hash, Debug,
134)]
135#[strum(serialize_all = "snake_case")]
136#[strum(prefix = "px_")]
137#[doc(hidden)]
138pub enum PXConfigKey {
139 #[strum(props(static = "true", required = "true"))]
140 AppId,
141 #[strum(props(sensitive = "true", static = "true", required = "true"))]
142 CookieSecret,
143 #[strum(props(sensitive = "true", static = "true", required = "true"))]
144 AuthToken,
145 Debug,
146 BlockingScore,
147 ModuleEnabled,
148 ModuleMode,
149 SensitiveHeaders,
150 SensitiveRoutes,
151 SensitiveRoutesRegex,
152 FilterByRoute,
153 FilterByExtension,
154 FilterByUserAgent,
155 FilterByIp,
156 FilterByHttpMethod,
157 CustomCookieHeader,
158 EnforcedRoutes,
159 MonitoredRoutes,
160 BypassMonitorHeader,
161 FirstPartyEnabled,
162 CustomLogo,
163 JsRef,
164 CssRef,
165 #[strum(props(local = "true"))]
166 HumanSapiHost,
167 #[strum(props(local = "true"))]
168 HumanSapiBackend,
169 #[strum(props(local = "true"))]
170 HumanCollectorHost,
171 #[strum(props(local = "true"))]
172 HumanCollectorBackend,
173 #[strum(props(local = "true"))]
174 HumanClientHost,
175 #[strum(props(local = "true"))]
176 HumanClientBackend,
177 #[strum(props(local = "true"))]
178 HumanCaptchaHost,
179 #[strum(props(local = "true"))]
180 HumanCaptchaBackend,
181 IpHeaders,
182 LogEndpoint,
183 DataEnrichmentHeaderName,
184 ExtractedCookies,
185 CorsSupportEnabled,
186 CorsPreflightRequestFilterEnabled,
187 GraphqlEnabled,
188 GraphqlRoutes,
189 SensitiveGraphqlOperationNames,
190 SensitiveGraphqlOperationTypes,
191 GraphqlBodyMaxLength,
192 GraphqlKeywords,
193 S2sTimeout,
194 TokenVersion,
195 CustomFirstPartyCaptchaEndpoint,
196 CustomFirstPartySensorEndpoint,
197 CustomFirstPartyXhrEndpoint,
198 UserAgentMaxLength,
199 RiskCookieMaxLength,
200 RiskCookieMinIterations,
201 RiskCookieMaxIterations,
202 AgenticTrustEnabled,
203 AgenticTrustMcpEndpointPath,
204 SecuredPxhdEnabled,
205 PxhdDomain,
206 JwtCookieName,
207 JwtCookieUserIdFieldName,
208 JwtCookieAdditionalFieldNames,
209 JwtHeaderName,
210 JwtHeaderUserIdFieldName,
211 JwtHeaderAdditionalFieldNames,
212 #[strum(props(sensitive = "true", static = "true"))]
213 LoggerAuthToken,
214 LoginCredentialsExtractionEnabled,
215 LoginCredentialsExtraction,
216 CredentialsIntelligenceVersion,
217 CompromisedCredentialsHeader,
218 SendRawUsernameOnAdditionalS2sActivity,
219 AdditionalS2sActivityEnabled,
220 AdditionalS2sActivityHeaderEnabled,
221 LoginSuccessfulReportingMethod,
222 LoginSuccessfulBodyRegex,
223 LoginSuccessfulHeaderName,
224 LoginSuccessfulHeaderValue,
225 LoginSuccessfulStatus,
226}
227
228impl PXConfigKey {
229 pub fn is_sensitive(&self) -> bool {
232 self.get_str("sensitive") == Some("true")
233 }
234
235 pub fn is_static(&self) -> bool {
238 self.get_str("static") == Some("true")
239 }
240
241 pub fn is_required(&self) -> bool {
244 self.get_str("required") == Some("true")
245 }
246
247 pub fn is_local(&self) -> bool {
250 self.get_str("local") == Some("true")
251 }
252}
253
254pub enum PXConfigValue<'a> {
258 Bool(bool),
259 U8(u8),
260 U16(u16),
261 U16Vec(&'a [u16]),
262 U32(u32),
263 USize(usize),
264 Str(&'a str),
265 StrVec(&'a [String]),
266 RegexVec(&'a [Regex]),
267 ModuleMode(PXModuleMode),
268 TokenVersion(TokenVersion),
269 CiEndpoints(&'a [PXCredentialEndpointConfig]),
270}
271
272impl fmt::Display for PXConfigValue<'_> {
273 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
274 match self {
275 Self::Bool(v) => write!(f, "Bool({v})"),
276 Self::U8(v) => write!(f, "U8({v})"),
277 Self::U16(v) => write!(f, "U16({v})"),
278 Self::U16Vec(v) => write!(f, "U16Vec({v:?})"),
279 Self::U32(v) => write!(f, "U32({v})"),
280 Self::USize(v) => write!(f, "USize({v})"),
281 Self::Str(v) => write!(f, "Str({v})"),
282 Self::StrVec(v) => write!(f, "StrVec({v:?})"),
283 Self::RegexVec(v) => {
284 let pats: Vec<&str> = v.iter().map(Regex::as_str).collect();
285 write!(f, "RegexVec({pats:?})")
286 }
287 Self::ModuleMode(v) => write!(f, "ModuleMode({})", v.as_ref()),
288 Self::TokenVersion(v) => write!(f, "TokenVersion({})", v.as_ref()),
289 Self::CiEndpoints(v) => write!(f, "CiEndpoints({v:?})"),
290 }
291 }
292}
293
294impl Serialize for PXConfigValue<'_> {
295 fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
296 match self {
297 Self::Bool(v) => v.serialize(s),
298 Self::U8(v) => v.serialize(s),
299 Self::U16(v) => v.serialize(s),
300 Self::U16Vec(v) => v.serialize(s),
301 Self::U32(v) => v.serialize(s),
302 Self::USize(v) => v.serialize(s),
303 Self::Str(v) => v.serialize(s),
304 Self::StrVec(v) => v.serialize(s),
305 Self::RegexVec(v) => {
306 let pats: Vec<String> = v
307 .iter()
308 .map(|r| format!("{REGEX_PREFIX}{}", r.as_str()))
309 .collect();
310 pats.serialize(s)
311 }
312 Self::ModuleMode(v) => v.serialize(s),
313 Self::TokenVersion(v) => v.serialize(s),
314 Self::CiEndpoints(v) => v.serialize(s),
315 }
316 }
317}
318
319const REDACTED: &str = "***REDACTED***";
320const REDACT_THRESHOLD: usize = 50;
321const REDACT_TAIL_LEN: usize = 5;
322pub(crate) const REGEX_PREFIX: &str = "_REGEXP ";
323
324fn redact(s: &str) -> String {
325 let char_count = s.chars().count();
326 if char_count >= REDACT_THRESHOLD {
327 let tail: String = s.chars().skip(char_count - REDACT_TAIL_LEN).collect();
328 format!("{REDACTED}{tail}")
329 } else if char_count > 0 {
330 REDACTED.to_owned()
331 } else {
332 s.to_owned()
333 }
334}
335
336struct Redacted<'a>(&'a PXConfigValue<'a>);
337
338impl Serialize for Redacted<'_> {
339 fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
340 match self.0 {
341 PXConfigValue::Bool(v) => redact(&v.to_string()).serialize(s),
342 PXConfigValue::U8(v) => redact(&v.to_string()).serialize(s),
343 PXConfigValue::U16(v) => redact(&v.to_string()).serialize(s),
344 PXConfigValue::U16Vec(v) => v
345 .iter()
346 .map(|n| redact(&n.to_string()))
347 .collect::<Vec<_>>()
348 .serialize(s),
349 PXConfigValue::U32(v) => redact(&v.to_string()).serialize(s),
350 PXConfigValue::USize(v) => redact(&v.to_string()).serialize(s),
351 PXConfigValue::Str(v) => redact(v).serialize(s),
352 PXConfigValue::StrVec(v) => v
353 .iter()
354 .map(|item| redact(item))
355 .collect::<Vec<_>>()
356 .serialize(s),
357 PXConfigValue::RegexVec(v) => v
358 .iter()
359 .map(|item| redact(item.as_str()))
360 .collect::<Vec<_>>()
361 .serialize(s),
362 PXConfigValue::ModuleMode(v) => redact(v.as_ref()).serialize(s),
363 PXConfigValue::TokenVersion(v) => redact(v.as_ref()).serialize(s),
364 PXConfigValue::CiEndpoints(v) => v.serialize(s),
365 }
366 }
367}
368
369impl Serialize for PXModuleMode {
370 fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
371 s.serialize_str(self.as_ref())
372 }
373}
374
375impl<'de> Deserialize<'de> for PXModuleMode {
376 fn deserialize<D: Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
377 let s = String::deserialize(d)?;
378 s.parse().map_err(de::Error::custom)
379 }
380}
381
382impl Serialize for TokenVersion {
383 fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
384 s.serialize_str(self.as_ref())
385 }
386}
387
388impl<'de> Deserialize<'de> for TokenVersion {
389 fn deserialize<D: Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
390 let s = String::deserialize(d)?;
391 s.parse().map_err(de::Error::custom)
392 }
393}
394
395pub struct PXConfig {
403 #[cfg(not(feature = "kv_store"))]
404 pub store: Option<ConfigStore>,
406 #[cfg(feature = "kv_store")]
407 pub store: Option<KVStore>,
409
410 pub app_id: String,
412 pub cookie_secret: String,
414 pub auth_token: String,
416 pub debug: bool,
418 pub blocking_score: u8,
420 pub module_enabled: bool,
422 pub module_mode: PXModuleMode,
424 pub sensitive_headers: Vec<String>,
426 pub sensitive_routes: Vec<String>,
428 pub sensitive_routes_regex: Vec<Regex>,
430 pub filter_by_route: Vec<String>,
432 pub filter_by_extension: Vec<String>,
434 pub filter_by_user_agent: Vec<String>,
436 pub filter_by_ip: Vec<String>,
438 pub filter_by_http_method: Vec<String>,
440 pub custom_cookie_header: String,
442 pub enforced_routes: Vec<String>,
444 pub monitored_routes: Vec<String>,
446 pub bypass_monitor_header: String,
448 pub first_party_enabled: bool,
450 pub custom_logo: String,
452 pub js_ref: String,
454 pub css_ref: String,
456 pub ip_headers: Vec<String>,
458 pub log_endpoint: String,
460 pub data_enrichment_header_name: String,
462 pub extracted_cookies: Vec<String>,
464 pub cors_support_enabled: bool,
466 pub cors_preflight_request_filter_enabled: bool,
468 pub graphql_enabled: bool,
470 pub graphql_routes: Vec<Regex>,
472 pub sensitive_graphql_operation_names: Vec<String>,
474 pub sensitive_graphql_operation_types: Vec<String>,
476 pub graphql_body_max_length: usize,
478 pub graphql_keywords: Vec<Regex>,
480 pub s2s_timeout: u32,
482 pub token_version: TokenVersion,
484 pub custom_first_party_captcha_endpoint: String,
486 pub custom_first_party_sensor_endpoint: String,
488 pub custom_first_party_xhr_endpoint: String,
490 pub user_agent_max_length: usize,
492 pub risk_cookie_max_length: usize,
494 pub risk_cookie_min_iterations: usize,
496 pub risk_cookie_max_iterations: usize,
498 pub agentic_trust_enabled: bool,
500 pub agentic_trust_mcp_endpoint_path: String,
502 pub logger_auth_token: String,
504 pub secured_pxhd_enabled: bool,
506 pub pxhd_domain: String,
508 pub jwt_cookie_name: String,
510 pub jwt_cookie_user_id_field_name: String,
512 pub jwt_cookie_additional_field_names: Vec<String>,
514 pub jwt_header_name: String,
516 pub jwt_header_user_id_field_name: String,
518 pub jwt_header_additional_field_names: Vec<String>,
520
521 pub human_sapi_host: String,
523 pub human_sapi_backend: String,
525 pub human_collector_host: String,
527 pub human_collector_backend: String,
529 pub human_client_host: String,
531 pub human_client_backend: String,
533 pub human_captcha_host: String,
535 pub human_captcha_backend: String,
537 pub login_credentials_extraction_enabled: bool,
539 pub login_credentials_extraction: Vec<PXCredentialEndpointConfig>,
541 pub prepared_ci_endpoints: Vec<PXPreparedCredentialEndpoint>,
543 pub credentials_intelligence_version: String,
545 pub compromised_credentials_header: String,
547 pub send_raw_username_on_additional_s2s_activity: bool,
549 pub additional_s2s_activity_enabled: bool,
551 pub additional_s2s_activity_header_enabled: bool,
553 pub login_successful_reporting_method: String,
555 pub login_successful_body_regex: String,
557 pub login_successful_header_name: String,
559 pub login_successful_header_value: String,
561 pub login_successful_status: Vec<u16>,
563
564 pub enrich_params_fn: Option<PXEnrichCustomParamsFn>,
567 pub is_sensitive_request_fn: Option<PXIsSensitiveRequestFn>,
569 pub is_enforced_request_fn: Option<PXIsEnforcedRequestFn>,
571 pub is_monitored_request_fn: Option<PXIsMonitoredRequestFn>,
573 pub is_filtered_request_fn: Option<PXIsFilteredRequestFn>,
575 pub additional_activity_handler_fn: Option<PXAdditionalActivityHandlerFn>,
577 pub cors_custom_preflight_handler_fn: Option<PXCorsCustomPreflightHandlerFn>,
579 pub cors_create_custom_block_response_headers_fn: Option<PXCorsCustomBlockResponseHeadersFn>,
581 pub ci_extract_credentials_fn: Option<PXExtractCredentialsFn>,
583 pub ci_login_successful_fn: Option<PXLoginSuccessfulFn>,
585}
586
587impl Default for PXConfig {
588 fn default() -> Self {
589 Self {
590 store: None,
591 app_id: String::new(),
592 cookie_secret: String::new(),
593 auth_token: String::new(),
594 debug: false,
595 blocking_score: 100,
596 module_enabled: false,
597 module_mode: PXModuleMode::Monitor,
598 sensitive_headers: vec!["Cookie".to_owned(), "Cookies".to_owned()],
599 sensitive_routes: Vec::new(),
600 sensitive_routes_regex: Vec::new(),
601 filter_by_route: Vec::new(),
602 filter_by_extension: WHITELIST_EXT.iter().map(|ext| (*ext).to_owned()).collect(),
603 filter_by_user_agent: Vec::new(),
604 filter_by_ip: Vec::new(),
605 filter_by_http_method: Vec::new(),
606 custom_cookie_header: CUSTOM_COOKIE_HEADER.to_owned(),
607 enforced_routes: Vec::new(),
608 monitored_routes: Vec::new(),
609 bypass_monitor_header: "x-px-block".to_owned(),
610 first_party_enabled: true,
611 custom_logo: String::new(),
612 js_ref: String::new(),
613 css_ref: String::new(),
614 human_sapi_host: default_human_sapi_host(""),
615 human_sapi_backend: "human_sapi".to_owned(),
616 human_collector_host: default_human_collector_host(""),
617 human_collector_backend: "human_collector".to_owned(),
618 human_client_host: "client.perimeterx.net".to_owned(),
619 human_client_backend: "human_client".to_owned(),
620 human_captcha_host: "captcha.px-cdn.net".to_owned(),
621 human_captcha_backend: "human_captcha".to_owned(),
622 ip_headers: Vec::new(),
623 log_endpoint: String::new(),
624 data_enrichment_header_name: String::new(),
625 extracted_cookies: Vec::new(),
626 cors_support_enabled: false,
627 cors_preflight_request_filter_enabled: false,
628 graphql_enabled: false,
629 graphql_routes: default_graphql_routes(),
630 sensitive_graphql_operation_names: Vec::new(),
631 sensitive_graphql_operation_types: Vec::new(),
632 graphql_body_max_length: GRAPHQL_BODY_MAX_LENGTH,
633 graphql_keywords: Vec::new(),
634 s2s_timeout: 2000,
635 token_version: TokenVersion::V3,
636 custom_first_party_captcha_endpoint: String::new(),
637 custom_first_party_sensor_endpoint: String::new(),
638 custom_first_party_xhr_endpoint: String::new(),
639 user_agent_max_length: 8528,
640 risk_cookie_max_length: 2048,
641 risk_cookie_min_iterations: 500,
642 risk_cookie_max_iterations: 5000,
643 agentic_trust_enabled: false,
644 agentic_trust_mcp_endpoint_path: MCP_ENDPOINT_PATH.to_owned(),
645 logger_auth_token: String::new(),
646 secured_pxhd_enabled: false,
647 pxhd_domain: String::new(),
648 jwt_cookie_name: String::new(),
649 jwt_cookie_user_id_field_name: String::new(),
650 jwt_cookie_additional_field_names: Vec::new(),
651 jwt_header_name: String::new(),
652 jwt_header_user_id_field_name: String::new(),
653 jwt_header_additional_field_names: Vec::new(),
654 login_credentials_extraction_enabled: false,
655 login_credentials_extraction: Vec::new(),
656 prepared_ci_endpoints: Vec::new(),
657 credentials_intelligence_version: "both".to_owned(),
658 compromised_credentials_header:
659 crate::modules::pxconstants::DEFAULT_COMPROMISED_CREDENTIALS_HEADER.to_owned(),
660 send_raw_username_on_additional_s2s_activity: false,
661 additional_s2s_activity_enabled: true,
662 additional_s2s_activity_header_enabled: false,
663 login_successful_reporting_method: "status".to_owned(),
664 login_successful_body_regex: String::new(),
665 login_successful_header_name: String::new(),
666 login_successful_header_value: String::new(),
667 login_successful_status: vec![200],
668 enrich_params_fn: None,
669 is_sensitive_request_fn: None,
670 is_enforced_request_fn: None,
671 is_monitored_request_fn: None,
672 is_filtered_request_fn: None,
673 additional_activity_handler_fn: None,
674 cors_custom_preflight_handler_fn: None,
675 cors_create_custom_block_response_headers_fn: None,
676 ci_extract_credentials_fn: None,
677 ci_login_successful_fn: None,
678 }
679 }
680}
681
682fn compile_ci_path_regex(path: &str) -> Option<Regex> {
683 let raw = path.strip_prefix(REGEX_PREFIX).unwrap_or(path);
684 match Regex::new(raw) {
685 Ok(re) => Some(re),
686 Err(e) => {
687 px_error!("Invalid CI endpoint regex {:?}: {}", raw, e);
688 None
689 }
690 }
691}
692
693pub(crate) fn rebuild_prepared_ci_endpoints(conf: &mut PXConfig) {
694 conf.prepared_ci_endpoints = conf
695 .login_credentials_extraction
696 .iter()
697 .map(|config| {
698 let path_regex = if config.path_type.eq_ignore_ascii_case("regex") {
699 compile_ci_path_regex(&config.path)
700 } else {
701 None
702 };
703 PXPreparedCredentialEndpoint {
704 config: config.clone(),
705 path_regex,
706 }
707 })
708 .collect();
709}
710
711fn is_empty_ci_endpoints_value(v: &serde_json::Value) -> bool {
712 match v {
713 serde_json::Value::Object(map) => map.is_empty(),
714 serde_json::Value::Array(arr) => {
715 arr.is_empty()
716 || arr
717 .iter()
718 .all(|item| item.as_object().is_some_and(serde_json::Map::is_empty))
719 }
720 _ => false,
721 }
722}
723
724fn parse_ci_endpoints_from_value(
725 v: &serde_json::Value,
726) -> Result<Vec<PXCredentialEndpointConfig>, serde_json::Error> {
727 if is_empty_ci_endpoints_value(v) {
728 return Ok(Vec::new());
729 }
730 if let Some(raw) = v.as_str() {
731 if raw.trim().is_empty() {
732 return Ok(Vec::new());
733 }
734 if let Ok(parsed) = serde_json::from_str::<serde_json::Value>(raw) {
735 return parse_ci_endpoints_from_value(&parsed);
736 }
737 serde_json::from_str(raw)
738 } else {
739 serde_json::from_value(v.clone())
740 }
741}
742
743fn parse_ci_endpoints(raw: &str) -> Vec<PXCredentialEndpointConfig> {
744 match parse_ci_endpoints_from_value(&serde_json::Value::String(raw.to_owned())) {
745 Ok(endpoints) => endpoints,
746 Err(e) => {
747 px_error!(
748 "failed to parse px_login_credentials_extraction from store: {}",
749 e
750 );
751 Vec::new()
752 }
753 }
754}
755
756fn parse_status_vec(raw: &str) -> Vec<u16> {
757 if let Ok(vec) = serde_json::from_str::<Vec<u16>>(raw) {
758 return vec;
759 }
760 raw.split(',')
761 .filter_map(|s| s.trim().parse::<u16>().ok())
762 .collect()
763}
764
765fn default_graphql_routes() -> Vec<Regex> {
766 Regex::new("^/graphql$")
767 .map(|r| vec![r])
768 .unwrap_or_default()
769}
770
771pub(crate) fn parse_vec(input: &str) -> Vec<String> {
775 if let Ok(vec) = serde_json::from_str::<Vec<String>>(input) {
776 return vec.into_iter().filter(|s| !s.is_empty()).collect();
777 }
778 input
779 .split(',')
780 .map(|s| s.trim())
781 .filter(|s| !s.is_empty())
782 .map(|s| s.to_owned())
783 .collect()
784}
785
786fn parse_regex_vec(key_name: &str, input: &str) -> Vec<Regex> {
787 let items: Vec<String> = if let Ok(vec) = serde_json::from_str::<Vec<String>>(input) {
788 vec.into_iter().filter(|s| !s.is_empty()).collect()
789 } else {
790 input
791 .split(',')
792 .map(|s| s.trim())
793 .filter(|s| !s.is_empty())
794 .map(|s| s.to_owned())
795 .collect()
796 };
797 items
798 .iter()
799 .filter_map(|s| match Regex::new(s) {
800 Ok(r) => Some(r),
801 Err(_) => {
802 px_error!("[{}] Invalid regex {:?}", key_name, s);
803 None
804 }
805 })
806 .collect()
807}
808
809#[cfg(feature = "kv_store")]
814pub(crate) fn json_value_to_raw_store(v: &serde_json::Value) -> String {
815 match v {
816 serde_json::Value::String(s) => s.clone(),
817 serde_json::Value::Array(arr) => {
818 let all_strings = arr.iter().all(serde_json::Value::is_string);
819 if all_strings {
820 arr.iter()
821 .filter_map(|item| item.as_str())
822 .collect::<Vec<_>>()
823 .join(",")
824 } else {
825 v.to_string()
826 }
827 }
828 other => other.to_string(),
829 }
830}
831
832pub(crate) fn strip_json_quotes(s: &str) -> &str {
835 s.strip_prefix('"')
836 .and_then(|inner| inner.strip_suffix('"'))
837 .unwrap_or(s)
838}
839
840fn parse_string(input: &str) -> String {
841 if input.eq_ignore_ascii_case("none") {
842 String::new()
843 } else {
844 input.to_owned()
845 }
846}
847
848fn default_human_sapi_host(app_id: &str) -> String {
849 format!("sapi-{app_id}.perimeterx.net")
850}
851
852fn default_human_collector_host(app_id: &str) -> String {
853 format!("collector-{app_id}.perimeterx.net")
854}
855
856fn compile_regex_vec<I, S>(patterns: I, key_name: &str) -> Vec<Regex>
861where
862 I: IntoIterator<Item = S>,
863 S: AsRef<str>,
864{
865 patterns
866 .into_iter()
867 .filter_map(|p| {
868 let raw = p.as_ref().strip_prefix(REGEX_PREFIX).unwrap_or(p.as_ref());
869 match Regex::new(raw) {
870 Ok(r) => Some(r),
871 Err(_) => {
872 px_error!("Invalid regex {:?} for key {}", raw, key_name);
873 None
874 }
875 }
876 })
877 .collect()
878}
879
880#[cfg(any(target_arch = "wasm32", test))]
883const SECRET_STORE_KEYS: &[PXConfigKey] = &[
884 PXConfigKey::AppId,
885 PXConfigKey::CookieSecret,
886 PXConfigKey::AuthToken,
887 PXConfigKey::LoggerAuthToken,
888];
889
890#[cfg(any(target_arch = "wasm32", test))]
891pub(crate) fn secret_store_keys() -> &'static [PXConfigKey] {
892 SECRET_STORE_KEYS
893}
894
895#[cfg(any(target_arch = "wasm32", test))]
898pub(crate) fn apply_secret_values(conf: &mut PXConfig, secrets: &[(PXConfigKey, &str)]) {
899 for (key, raw) in secrets {
900 conf.set_from_raw(*key, raw);
901 }
902}
903
904fn config_value_is_set(value: &PXConfigValue<'_>) -> bool {
905 match value {
906 PXConfigValue::Str(v) => !v.is_empty(),
907 PXConfigValue::StrVec(v) => !v.is_empty(),
908 PXConfigValue::RegexVec(v) => !v.is_empty(),
909 PXConfigValue::U16Vec(v) => !v.is_empty(),
910 PXConfigValue::CiEndpoints(v) => !v.is_empty(),
911 PXConfigValue::Bool(_)
912 | PXConfigValue::U8(_)
913 | PXConfigValue::U16(_)
914 | PXConfigValue::U32(_)
915 | PXConfigValue::USize(_)
916 | PXConfigValue::ModuleMode(_)
917 | PXConfigValue::TokenVersion(_) => true,
918 }
919}
920
921pub(crate) fn load_secrets_from_store(conf: &mut PXConfig, secret_store_name: &str) {
922 if secret_store_name.is_empty() {
923 return;
924 }
925
926 #[cfg(not(target_arch = "wasm32"))]
927 {
928 let _ = conf;
929 }
930
931 #[cfg(target_arch = "wasm32")]
932 let secret_store = match SecretStore::open(secret_store_name) {
933 Ok(store) => store,
934 Err(e) => {
935 px_error!("Failed to open Secret Store {:?}: {}", secret_store_name, e);
936 return;
937 }
938 };
939
940 #[cfg(target_arch = "wasm32")]
941 {
942 let mut overlay = Vec::with_capacity(secret_store_keys().len());
943 for key in secret_store_keys() {
944 let secret_name = key.as_ref();
945 match secret_store.try_get(secret_name) {
946 Ok(Some(secret)) => match secret.try_plaintext() {
947 Ok(plaintext) => {
948 if let Ok(value) = std::str::from_utf8(plaintext.as_ref()) {
949 overlay.push((*key, value.to_owned()));
950 } else {
951 px_error!(
952 "Secret {:?} in Secret Store {:?} is not valid UTF-8",
953 secret_name,
954 secret_store_name
955 );
956 }
957 }
958 Err(e) => {
959 px_error!(
960 "Failed to decrypt secret {:?} in Secret Store {:?}: {}",
961 secret_name,
962 secret_store_name,
963 e
964 );
965 }
966 },
967 Ok(None) => {}
968 Err(e) => {
969 px_error!(
970 "Failed to lookup secret {:?} in Secret Store {:?}: {}",
971 secret_name,
972 secret_store_name,
973 e
974 );
975 }
976 }
977 }
978
979 let overlay_refs: Vec<(PXConfigKey, &str)> = overlay
980 .iter()
981 .map(|(key, value)| (*key, value.as_str()))
982 .collect();
983 apply_secret_values(conf, &overlay_refs);
984 }
985}
986
987pub(crate) fn recompute_derived(conf: &mut PXConfig, previous_app_id: &str) {
988 let previous_sapi_host = default_human_sapi_host(previous_app_id);
989 if conf.human_sapi_host.is_empty() || conf.human_sapi_host == previous_sapi_host {
990 conf.human_sapi_host = default_human_sapi_host(&conf.app_id);
991 }
992
993 let previous_collector_host = default_human_collector_host(previous_app_id);
994 if conf.human_collector_host.is_empty() || conf.human_collector_host == previous_collector_host
995 {
996 conf.human_collector_host = default_human_collector_host(&conf.app_id);
997 }
998}
999
1000impl Serialize for PXConfig {
1001 fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
1002 let entries: Vec<_> = self.fields().filter(|(k, _)| !k.is_local()).collect();
1003 let mut map = s.serialize_map(Some(entries.len()))?;
1004 for (key, value) in entries {
1005 if key.is_sensitive() {
1006 map.serialize_entry(key.as_ref(), &Redacted(&value))?;
1007 } else {
1008 map.serialize_entry(key.as_ref(), &value)?;
1009 }
1010 }
1011 map.end()
1012 }
1013}
1014
1015impl<'de> Deserialize<'de> for PXConfig {
1016 fn deserialize<D: Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
1017 struct PXConfigVisitor;
1018
1019 impl<'de> Visitor<'de> for PXConfigVisitor {
1020 type Value = PXConfig;
1021
1022 fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
1023 f.write_str("a PXConfig map keyed by PXConfigKey names")
1024 }
1025
1026 fn visit_map<M: MapAccess<'de>>(self, mut map: M) -> Result<PXConfig, M::Error> {
1027 let mut out = PXConfig::default();
1028 let mut seen: HashSet<PXConfigKey> = HashSet::new();
1032
1033 while let Some(key) = map.next_key::<String>()? {
1034 let variant = PXConfigKey::iter()
1035 .find(|v| v.as_ref() == key)
1036 .ok_or_else(|| de::Error::custom(format!("unknown field `{key}`")))?;
1037 if !seen.insert(variant) {
1038 return Err(de::Error::custom(format!("duplicate field `{key}`")));
1039 }
1040 out.set_from(variant, &mut map)?;
1041 }
1042
1043 if let Some(missing) =
1044 PXConfigKey::iter().find(|v| !v.is_local() && !seen.contains(v))
1045 {
1046 return Err(de::Error::custom(format!(
1047 "missing field `{}`",
1048 missing.as_ref()
1049 )));
1050 }
1051
1052 rebuild_prepared_ci_endpoints(&mut out);
1053 recompute_derived(&mut out, "");
1054 Ok(out)
1055 }
1056 }
1057
1058 d.deserialize_map(PXConfigVisitor)
1059 }
1060}
1061
1062impl PXConfig {
1063 pub fn get_module_mode(&self) -> PXModuleMode {
1064 self.module_mode
1065 }
1066
1067 pub fn new(config_store_name: &str, secret_store_name: &str) -> Self {
1072 let mut conf = PXConfig::default();
1073
1074 #[cfg(feature = "kv_store")]
1075 let confg_store = {
1076 match KVStore::open(config_store_name) {
1077 Ok(Some(kv)) => kv,
1078 Ok(None) => {
1079 px_error!(
1080 "KVStore {:?} not found; falling back to default PXConfig",
1081 config_store_name
1082 );
1083 return Self::default();
1084 }
1085 Err(e) => {
1086 px_error!(
1087 "Failed to open KVStore {:?}: {:?}; falling back to default PXConfig",
1088 config_store_name,
1089 e
1090 );
1091 return Self::default();
1092 }
1093 }
1094 };
1095
1096 #[cfg(not(feature = "kv_store"))]
1097 let confg_store = ConfigStore::open(config_store_name);
1098
1099 for key in PXConfigKey::iter() {
1100 if let Some(raw) = confg_store.get(key.as_ref()) {
1101 conf.set_from_raw(key, &raw);
1102 }
1103 }
1104
1105 recompute_derived(&mut conf, "");
1106 conf.store = Some(confg_store);
1107 let app_id_before_secrets = conf.app_id.clone();
1108 load_secrets_from_store(&mut conf, secret_store_name);
1109 rebuild_prepared_ci_endpoints(&mut conf);
1110 recompute_derived(&mut conf, &app_id_before_secrets);
1111 conf.validate_required_fields();
1112 conf
1113 }
1114
1115 #[doc(hidden)]
1119 pub fn update_from_json(&mut self, cfg: &serde_json::Value) {
1120 let config_obj = cfg.get("configValue").unwrap_or(cfg);
1121
1122 let previous_app_id = self.app_id.clone();
1123 let mut config_found = false;
1124 for key in PXConfigKey::iter() {
1125 let Some(v) = config_obj.get(key.as_ref()).filter(|v| !v.is_null()) else {
1126 continue;
1127 };
1128
1129 config_found = true;
1130 if let Err(e) = self.set_from_value(key, v) {
1131 px_error!("update_from_json: bad value for {}: {}", key.as_ref(), e);
1132 }
1133
1134 #[cfg(feature = "kv_store")]
1135 if let Some(store) = self.store.as_mut() {
1136 let store_value = json_value_to_raw_store(v);
1137 let _ = store.insert(key.as_ref(), store_value);
1138 }
1139 }
1140
1141 if !config_found {
1142 px_error!("update_from_json: config not found in body");
1143 }
1144 rebuild_prepared_ci_endpoints(self);
1145 recompute_derived(self, &previous_app_id);
1146 self.validate_required_fields();
1147 }
1148
1149 pub fn get(&self, key: PXConfigKey) -> PXConfigValue<'_> {
1151 self.get_value(key)
1152 }
1153
1154 pub fn fields(&self) -> impl ExactSizeIterator<Item = (PXConfigKey, PXConfigValue<'_>)> {
1156 PXConfigKey::iter().map(|k| (k, self.get_value(k)))
1157 }
1158
1159 pub fn get_field(&self, name: impl AsRef<str>) -> Option<PXConfigValue<'_>> {
1161 let name = name.as_ref();
1162 self.fields()
1163 .find_map(|(k, v)| (k.as_ref() == name).then_some(v))
1164 }
1165
1166 pub(crate) fn build_static_json(&self) -> serde_json::Value {
1170 let static_fields: Vec<(PXConfigKey, PXConfigValue<'_>)> =
1171 self.fields().filter(|(k, _)| k.is_static()).collect();
1172 let mut map = serde_json::Map::with_capacity(static_fields.len());
1173 for (key, value) in static_fields {
1174 let json_value = if key.is_sensitive() {
1175 serde_json::to_value(Redacted(&value)).unwrap_or_default()
1176 } else {
1177 serde_json::to_value(&value).unwrap_or_default()
1178 };
1179 map.insert(key.as_ref().to_owned(), json_value);
1180 }
1181 serde_json::Value::Object(map)
1182 }
1183
1184 pub(crate) fn build_config_json(&self) -> serde_json::Value {
1188 let entries: Vec<(PXConfigKey, PXConfigValue<'_>)> =
1189 self.fields().filter(|(k, _)| !k.is_local()).collect();
1190 let mut map = serde_json::Map::with_capacity(entries.len());
1191 for (key, value) in entries {
1192 let json_value = if key.is_sensitive() {
1193 serde_json::to_value(Redacted(&value)).unwrap_or_default()
1194 } else {
1195 serde_json::to_value(&value).unwrap_or_default()
1196 };
1197 map.insert(key.as_ref().to_owned(), json_value);
1198 }
1199 serde_json::Value::Object(map)
1200 }
1201
1202 fn missing_required_fields(&self) -> Vec<PXConfigKey> {
1203 self.fields()
1204 .filter_map(|(key, value)| {
1205 (key.is_required() && !config_value_is_set(&value)).then_some(key)
1206 })
1207 .collect()
1208 }
1209
1210 fn validate_required_fields(&self) {
1211 for key in self.missing_required_fields() {
1212 px_error!(
1213 "Required PX configuration key {} is missing or empty",
1214 key.as_ref()
1215 );
1216 }
1217 }
1218
1219 fn get_value(&self, key: PXConfigKey) -> PXConfigValue<'_> {
1220 match key {
1221 PXConfigKey::AppId => PXConfigValue::Str(&self.app_id),
1222 PXConfigKey::CookieSecret => PXConfigValue::Str(&self.cookie_secret),
1223 PXConfigKey::AuthToken => PXConfigValue::Str(&self.auth_token),
1224 PXConfigKey::Debug => PXConfigValue::Bool(self.debug),
1225 PXConfigKey::BlockingScore => PXConfigValue::U8(self.blocking_score),
1226 PXConfigKey::ModuleEnabled => PXConfigValue::Bool(self.module_enabled),
1227 PXConfigKey::ModuleMode => PXConfigValue::ModuleMode(self.module_mode),
1228 PXConfigKey::SensitiveHeaders => PXConfigValue::StrVec(&self.sensitive_headers),
1229 PXConfigKey::SensitiveRoutes => PXConfigValue::StrVec(&self.sensitive_routes),
1230 PXConfigKey::SensitiveRoutesRegex => {
1231 PXConfigValue::RegexVec(&self.sensitive_routes_regex)
1232 }
1233 PXConfigKey::FilterByRoute => PXConfigValue::StrVec(&self.filter_by_route),
1234 PXConfigKey::FilterByExtension => PXConfigValue::StrVec(&self.filter_by_extension),
1235 PXConfigKey::FilterByUserAgent => PXConfigValue::StrVec(&self.filter_by_user_agent),
1236 PXConfigKey::FilterByIp => PXConfigValue::StrVec(&self.filter_by_ip),
1237 PXConfigKey::FilterByHttpMethod => PXConfigValue::StrVec(&self.filter_by_http_method),
1238 PXConfigKey::CustomCookieHeader => PXConfigValue::Str(&self.custom_cookie_header),
1239 PXConfigKey::EnforcedRoutes => PXConfigValue::StrVec(&self.enforced_routes),
1240 PXConfigKey::MonitoredRoutes => PXConfigValue::StrVec(&self.monitored_routes),
1241 PXConfigKey::BypassMonitorHeader => PXConfigValue::Str(&self.bypass_monitor_header),
1242 PXConfigKey::FirstPartyEnabled => PXConfigValue::Bool(self.first_party_enabled),
1243 PXConfigKey::CustomLogo => PXConfigValue::Str(&self.custom_logo),
1244 PXConfigKey::JsRef => PXConfigValue::Str(&self.js_ref),
1245 PXConfigKey::CssRef => PXConfigValue::Str(&self.css_ref),
1246 PXConfigKey::HumanSapiHost => PXConfigValue::Str(&self.human_sapi_host),
1247 PXConfigKey::HumanSapiBackend => PXConfigValue::Str(&self.human_sapi_backend),
1248 PXConfigKey::HumanCollectorHost => PXConfigValue::Str(&self.human_collector_host),
1249 PXConfigKey::HumanCollectorBackend => PXConfigValue::Str(&self.human_collector_backend),
1250 PXConfigKey::HumanClientHost => PXConfigValue::Str(&self.human_client_host),
1251 PXConfigKey::HumanClientBackend => PXConfigValue::Str(&self.human_client_backend),
1252 PXConfigKey::HumanCaptchaHost => PXConfigValue::Str(&self.human_captcha_host),
1253 PXConfigKey::HumanCaptchaBackend => PXConfigValue::Str(&self.human_captcha_backend),
1254 PXConfigKey::IpHeaders => PXConfigValue::StrVec(&self.ip_headers),
1255 PXConfigKey::LogEndpoint => PXConfigValue::Str(&self.log_endpoint),
1256 PXConfigKey::DataEnrichmentHeaderName => {
1257 PXConfigValue::Str(&self.data_enrichment_header_name)
1258 }
1259 PXConfigKey::ExtractedCookies => PXConfigValue::StrVec(&self.extracted_cookies),
1260 PXConfigKey::CorsSupportEnabled => PXConfigValue::Bool(self.cors_support_enabled),
1261 PXConfigKey::CorsPreflightRequestFilterEnabled => {
1262 PXConfigValue::Bool(self.cors_preflight_request_filter_enabled)
1263 }
1264 PXConfigKey::GraphqlEnabled => PXConfigValue::Bool(self.graphql_enabled),
1265 PXConfigKey::GraphqlRoutes => PXConfigValue::RegexVec(&self.graphql_routes),
1266 PXConfigKey::SensitiveGraphqlOperationNames => {
1267 PXConfigValue::StrVec(&self.sensitive_graphql_operation_names)
1268 }
1269 PXConfigKey::SensitiveGraphqlOperationTypes => {
1270 PXConfigValue::StrVec(&self.sensitive_graphql_operation_types)
1271 }
1272 PXConfigKey::GraphqlBodyMaxLength => PXConfigValue::USize(self.graphql_body_max_length),
1273 PXConfigKey::GraphqlKeywords => PXConfigValue::RegexVec(&self.graphql_keywords),
1274 PXConfigKey::S2sTimeout => PXConfigValue::U32(self.s2s_timeout),
1275 PXConfigKey::TokenVersion => PXConfigValue::TokenVersion(self.token_version),
1276 PXConfigKey::CustomFirstPartyCaptchaEndpoint => {
1277 PXConfigValue::Str(&self.custom_first_party_captcha_endpoint)
1278 }
1279 PXConfigKey::CustomFirstPartySensorEndpoint => {
1280 PXConfigValue::Str(&self.custom_first_party_sensor_endpoint)
1281 }
1282 PXConfigKey::CustomFirstPartyXhrEndpoint => {
1283 PXConfigValue::Str(&self.custom_first_party_xhr_endpoint)
1284 }
1285 PXConfigKey::UserAgentMaxLength => PXConfigValue::USize(self.user_agent_max_length),
1286 PXConfigKey::RiskCookieMaxLength => PXConfigValue::USize(self.risk_cookie_max_length),
1287 PXConfigKey::RiskCookieMinIterations => {
1288 PXConfigValue::USize(self.risk_cookie_min_iterations)
1289 }
1290 PXConfigKey::RiskCookieMaxIterations => {
1291 PXConfigValue::USize(self.risk_cookie_max_iterations)
1292 }
1293 PXConfigKey::AgenticTrustEnabled => PXConfigValue::Bool(self.agentic_trust_enabled),
1294 PXConfigKey::AgenticTrustMcpEndpointPath => {
1295 PXConfigValue::Str(&self.agentic_trust_mcp_endpoint_path)
1296 }
1297 PXConfigKey::SecuredPxhdEnabled => PXConfigValue::Bool(self.secured_pxhd_enabled),
1298 PXConfigKey::PxhdDomain => PXConfigValue::Str(&self.pxhd_domain),
1299 PXConfigKey::JwtCookieName => PXConfigValue::Str(&self.jwt_cookie_name),
1300 PXConfigKey::JwtCookieUserIdFieldName => {
1301 PXConfigValue::Str(&self.jwt_cookie_user_id_field_name)
1302 }
1303 PXConfigKey::JwtCookieAdditionalFieldNames => {
1304 PXConfigValue::StrVec(&self.jwt_cookie_additional_field_names)
1305 }
1306 PXConfigKey::JwtHeaderName => PXConfigValue::Str(&self.jwt_header_name),
1307 PXConfigKey::JwtHeaderUserIdFieldName => {
1308 PXConfigValue::Str(&self.jwt_header_user_id_field_name)
1309 }
1310 PXConfigKey::JwtHeaderAdditionalFieldNames => {
1311 PXConfigValue::StrVec(&self.jwt_header_additional_field_names)
1312 }
1313 PXConfigKey::LoggerAuthToken => PXConfigValue::Str(&self.logger_auth_token),
1314 PXConfigKey::LoginCredentialsExtractionEnabled => {
1315 PXConfigValue::Bool(self.login_credentials_extraction_enabled)
1316 }
1317 PXConfigKey::LoginCredentialsExtraction => {
1318 PXConfigValue::CiEndpoints(&self.login_credentials_extraction)
1319 }
1320 PXConfigKey::CredentialsIntelligenceVersion => {
1321 PXConfigValue::Str(&self.credentials_intelligence_version)
1322 }
1323 PXConfigKey::CompromisedCredentialsHeader => {
1324 PXConfigValue::Str(&self.compromised_credentials_header)
1325 }
1326 PXConfigKey::SendRawUsernameOnAdditionalS2sActivity => {
1327 PXConfigValue::Bool(self.send_raw_username_on_additional_s2s_activity)
1328 }
1329 PXConfigKey::AdditionalS2sActivityEnabled => {
1330 PXConfigValue::Bool(self.additional_s2s_activity_enabled)
1331 }
1332 PXConfigKey::AdditionalS2sActivityHeaderEnabled => {
1333 PXConfigValue::Bool(self.additional_s2s_activity_header_enabled)
1334 }
1335 PXConfigKey::LoginSuccessfulReportingMethod => {
1336 PXConfigValue::Str(&self.login_successful_reporting_method)
1337 }
1338 PXConfigKey::LoginSuccessfulBodyRegex => {
1339 PXConfigValue::Str(&self.login_successful_body_regex)
1340 }
1341 PXConfigKey::LoginSuccessfulHeaderName => {
1342 PXConfigValue::Str(&self.login_successful_header_name)
1343 }
1344 PXConfigKey::LoginSuccessfulHeaderValue => {
1345 PXConfigValue::Str(&self.login_successful_header_value)
1346 }
1347 PXConfigKey::LoginSuccessfulStatus => {
1348 PXConfigValue::U16Vec(&self.login_successful_status)
1349 }
1350 }
1351 }
1352
1353 fn set_from<'de, M: MapAccess<'de>>(
1354 &mut self,
1355 key: PXConfigKey,
1356 map: &mut M,
1357 ) -> Result<(), M::Error> {
1358 match key {
1359 PXConfigKey::AppId => self.app_id = map.next_value()?,
1360 PXConfigKey::CookieSecret => self.cookie_secret = map.next_value()?,
1361 PXConfigKey::AuthToken => self.auth_token = map.next_value()?,
1362 PXConfigKey::Debug => self.debug = map.next_value()?,
1363 PXConfigKey::BlockingScore => self.blocking_score = map.next_value()?,
1364 PXConfigKey::ModuleEnabled => self.module_enabled = map.next_value()?,
1365 PXConfigKey::ModuleMode => self.module_mode = map.next_value()?,
1366 PXConfigKey::SensitiveHeaders => self.sensitive_headers = map.next_value()?,
1367 PXConfigKey::SensitiveRoutes => self.sensitive_routes = map.next_value()?,
1368 PXConfigKey::SensitiveRoutesRegex => {
1369 let pats: Vec<String> = map.next_value()?;
1370 self.sensitive_routes_regex = compile_regex_vec(pats, key.as_ref());
1371 }
1372 PXConfigKey::FilterByRoute => self.filter_by_route = map.next_value()?,
1373 PXConfigKey::FilterByExtension => self.filter_by_extension = map.next_value()?,
1374 PXConfigKey::FilterByUserAgent => self.filter_by_user_agent = map.next_value()?,
1375 PXConfigKey::FilterByIp => self.filter_by_ip = map.next_value()?,
1376 PXConfigKey::FilterByHttpMethod => self.filter_by_http_method = map.next_value()?,
1377 PXConfigKey::CustomCookieHeader => self.custom_cookie_header = map.next_value()?,
1378 PXConfigKey::EnforcedRoutes => self.enforced_routes = map.next_value()?,
1379 PXConfigKey::MonitoredRoutes => self.monitored_routes = map.next_value()?,
1380 PXConfigKey::BypassMonitorHeader => self.bypass_monitor_header = map.next_value()?,
1381 PXConfigKey::FirstPartyEnabled => self.first_party_enabled = map.next_value()?,
1382 PXConfigKey::CustomLogo => self.custom_logo = map.next_value()?,
1383 PXConfigKey::JsRef => self.js_ref = map.next_value()?,
1384 PXConfigKey::CssRef => self.css_ref = map.next_value()?,
1385 PXConfigKey::HumanSapiHost => self.human_sapi_host = map.next_value()?,
1386 PXConfigKey::HumanSapiBackend => self.human_sapi_backend = map.next_value()?,
1387 PXConfigKey::HumanCollectorHost => self.human_collector_host = map.next_value()?,
1388 PXConfigKey::HumanCollectorBackend => {
1389 self.human_collector_backend = map.next_value()?
1390 }
1391 PXConfigKey::HumanClientHost => self.human_client_host = map.next_value()?,
1392 PXConfigKey::HumanClientBackend => self.human_client_backend = map.next_value()?,
1393 PXConfigKey::HumanCaptchaHost => self.human_captcha_host = map.next_value()?,
1394 PXConfigKey::HumanCaptchaBackend => self.human_captcha_backend = map.next_value()?,
1395 PXConfigKey::IpHeaders => self.ip_headers = map.next_value()?,
1396 PXConfigKey::LogEndpoint => self.log_endpoint = map.next_value()?,
1397 PXConfigKey::DataEnrichmentHeaderName => {
1398 self.data_enrichment_header_name = map.next_value()?
1399 }
1400 PXConfigKey::ExtractedCookies => self.extracted_cookies = map.next_value()?,
1401 PXConfigKey::CorsSupportEnabled => self.cors_support_enabled = map.next_value()?,
1402 PXConfigKey::CorsPreflightRequestFilterEnabled => {
1403 self.cors_preflight_request_filter_enabled = map.next_value()?
1404 }
1405 PXConfigKey::GraphqlEnabled => self.graphql_enabled = map.next_value()?,
1406 PXConfigKey::GraphqlRoutes => {
1407 let pats: Vec<String> = map.next_value()?;
1408 self.graphql_routes = compile_regex_vec(pats, key.as_ref());
1409 }
1410 PXConfigKey::SensitiveGraphqlOperationNames => {
1411 self.sensitive_graphql_operation_names = map.next_value()?
1412 }
1413 PXConfigKey::SensitiveGraphqlOperationTypes => {
1414 self.sensitive_graphql_operation_types = map.next_value()?
1415 }
1416 PXConfigKey::GraphqlBodyMaxLength => self.graphql_body_max_length = map.next_value()?,
1417 PXConfigKey::GraphqlKeywords => {
1418 let pats: Vec<String> = map.next_value()?;
1419 self.graphql_keywords = compile_regex_vec(pats, key.as_ref());
1420 }
1421 PXConfigKey::S2sTimeout => self.s2s_timeout = map.next_value()?,
1422 PXConfigKey::TokenVersion => self.token_version = map.next_value()?,
1423 PXConfigKey::CustomFirstPartyCaptchaEndpoint => {
1424 self.custom_first_party_captcha_endpoint = map.next_value()?
1425 }
1426 PXConfigKey::CustomFirstPartySensorEndpoint => {
1427 self.custom_first_party_sensor_endpoint = map.next_value()?
1428 }
1429 PXConfigKey::CustomFirstPartyXhrEndpoint => {
1430 self.custom_first_party_xhr_endpoint = map.next_value()?
1431 }
1432 PXConfigKey::UserAgentMaxLength => self.user_agent_max_length = map.next_value()?,
1433 PXConfigKey::RiskCookieMaxLength => self.risk_cookie_max_length = map.next_value()?,
1434 PXConfigKey::RiskCookieMinIterations => {
1435 self.risk_cookie_min_iterations = map.next_value()?
1436 }
1437 PXConfigKey::RiskCookieMaxIterations => {
1438 self.risk_cookie_max_iterations = map.next_value()?
1439 }
1440 PXConfigKey::AgenticTrustEnabled => self.agentic_trust_enabled = map.next_value()?,
1441 PXConfigKey::AgenticTrustMcpEndpointPath => {
1442 self.agentic_trust_mcp_endpoint_path = map.next_value()?
1443 }
1444 PXConfigKey::SecuredPxhdEnabled => self.secured_pxhd_enabled = map.next_value()?,
1445 PXConfigKey::PxhdDomain => self.pxhd_domain = map.next_value()?,
1446 PXConfigKey::JwtCookieName => self.jwt_cookie_name = map.next_value()?,
1447 PXConfigKey::JwtCookieUserIdFieldName => {
1448 self.jwt_cookie_user_id_field_name = map.next_value()?
1449 }
1450 PXConfigKey::JwtCookieAdditionalFieldNames => {
1451 self.jwt_cookie_additional_field_names = map.next_value()?
1452 }
1453 PXConfigKey::JwtHeaderName => self.jwt_header_name = map.next_value()?,
1454 PXConfigKey::JwtHeaderUserIdFieldName => {
1455 self.jwt_header_user_id_field_name = map.next_value()?
1456 }
1457 PXConfigKey::JwtHeaderAdditionalFieldNames => {
1458 self.jwt_header_additional_field_names = map.next_value()?
1459 }
1460 PXConfigKey::LoggerAuthToken => self.logger_auth_token = map.next_value()?,
1461 PXConfigKey::LoginCredentialsExtractionEnabled => {
1462 self.login_credentials_extraction_enabled = map.next_value()?
1463 }
1464 PXConfigKey::LoginCredentialsExtraction => {
1465 let v: serde_json::Value = map.next_value()?;
1466 self.login_credentials_extraction =
1467 parse_ci_endpoints_from_value(&v).map_err(serde::de::Error::custom)?;
1468 rebuild_prepared_ci_endpoints(self);
1469 }
1470 PXConfigKey::CredentialsIntelligenceVersion => {
1471 self.credentials_intelligence_version = map.next_value()?
1472 }
1473 PXConfigKey::CompromisedCredentialsHeader => {
1474 self.compromised_credentials_header = map.next_value()?
1475 }
1476 PXConfigKey::SendRawUsernameOnAdditionalS2sActivity => {
1477 self.send_raw_username_on_additional_s2s_activity = map.next_value()?
1478 }
1479 PXConfigKey::AdditionalS2sActivityEnabled => {
1480 self.additional_s2s_activity_enabled = map.next_value()?
1481 }
1482 PXConfigKey::AdditionalS2sActivityHeaderEnabled => {
1483 self.additional_s2s_activity_header_enabled = map.next_value()?
1484 }
1485 PXConfigKey::LoginSuccessfulReportingMethod => {
1486 self.login_successful_reporting_method = map.next_value()?
1487 }
1488 PXConfigKey::LoginSuccessfulBodyRegex => {
1489 self.login_successful_body_regex = map.next_value()?
1490 }
1491 PXConfigKey::LoginSuccessfulHeaderName => {
1492 self.login_successful_header_name = map.next_value()?
1493 }
1494 PXConfigKey::LoginSuccessfulHeaderValue => {
1495 self.login_successful_header_value = map.next_value()?
1496 }
1497 PXConfigKey::LoginSuccessfulStatus => {
1498 self.login_successful_status = map.next_value()?
1499 }
1500 }
1501 Ok(())
1502 }
1503
1504 pub(crate) fn set_from_value(
1505 &mut self,
1506 key: PXConfigKey,
1507 v: &serde_json::Value,
1508 ) -> Result<(), serde_json::Error> {
1509 match key {
1510 PXConfigKey::AppId => self.app_id = serde_json::from_value(v.clone())?,
1511 PXConfigKey::CookieSecret => self.cookie_secret = serde_json::from_value(v.clone())?,
1512 PXConfigKey::AuthToken => self.auth_token = serde_json::from_value(v.clone())?,
1513 PXConfigKey::Debug => self.debug = serde_json::from_value(v.clone())?,
1514 PXConfigKey::BlockingScore => self.blocking_score = serde_json::from_value(v.clone())?,
1515 PXConfigKey::ModuleEnabled => self.module_enabled = serde_json::from_value(v.clone())?,
1516 PXConfigKey::ModuleMode => self.module_mode = serde_json::from_value(v.clone())?,
1517 PXConfigKey::SensitiveHeaders => {
1518 self.sensitive_headers = serde_json::from_value(v.clone())?
1519 }
1520 PXConfigKey::SensitiveRoutes => {
1521 self.sensitive_routes = serde_json::from_value(v.clone())?
1522 }
1523 PXConfigKey::SensitiveRoutesRegex => {
1524 let pats: Vec<String> = serde_json::from_value(v.clone())?;
1525 self.sensitive_routes_regex = compile_regex_vec(pats, key.as_ref());
1526 }
1527 PXConfigKey::FilterByRoute => self.filter_by_route = serde_json::from_value(v.clone())?,
1528 PXConfigKey::FilterByExtension => {
1529 self.filter_by_extension = serde_json::from_value(v.clone())?
1530 }
1531 PXConfigKey::FilterByUserAgent => {
1532 self.filter_by_user_agent = serde_json::from_value(v.clone())?
1533 }
1534 PXConfigKey::FilterByIp => self.filter_by_ip = serde_json::from_value(v.clone())?,
1535 PXConfigKey::FilterByHttpMethod => {
1536 self.filter_by_http_method = serde_json::from_value(v.clone())?
1537 }
1538 PXConfigKey::CustomCookieHeader => {
1539 self.custom_cookie_header = serde_json::from_value(v.clone())?
1540 }
1541 PXConfigKey::EnforcedRoutes => {
1542 self.enforced_routes = serde_json::from_value(v.clone())?
1543 }
1544 PXConfigKey::MonitoredRoutes => {
1545 self.monitored_routes = serde_json::from_value(v.clone())?
1546 }
1547 PXConfigKey::BypassMonitorHeader => {
1548 self.bypass_monitor_header = serde_json::from_value(v.clone())?
1549 }
1550 PXConfigKey::FirstPartyEnabled => {
1551 self.first_party_enabled = serde_json::from_value(v.clone())?
1552 }
1553 PXConfigKey::CustomLogo => self.custom_logo = serde_json::from_value(v.clone())?,
1554 PXConfigKey::JsRef => self.js_ref = serde_json::from_value(v.clone())?,
1555 PXConfigKey::CssRef => self.css_ref = serde_json::from_value(v.clone())?,
1556 PXConfigKey::HumanSapiHost => self.human_sapi_host = serde_json::from_value(v.clone())?,
1557 PXConfigKey::HumanSapiBackend => {
1558 self.human_sapi_backend = serde_json::from_value(v.clone())?
1559 }
1560 PXConfigKey::HumanCollectorHost => {
1561 self.human_collector_host = serde_json::from_value(v.clone())?
1562 }
1563 PXConfigKey::HumanCollectorBackend => {
1564 self.human_collector_backend = serde_json::from_value(v.clone())?
1565 }
1566 PXConfigKey::HumanClientHost => {
1567 self.human_client_host = serde_json::from_value(v.clone())?
1568 }
1569 PXConfigKey::HumanClientBackend => {
1570 self.human_client_backend = serde_json::from_value(v.clone())?
1571 }
1572 PXConfigKey::HumanCaptchaHost => {
1573 self.human_captcha_host = serde_json::from_value(v.clone())?
1574 }
1575 PXConfigKey::HumanCaptchaBackend => {
1576 self.human_captcha_backend = serde_json::from_value(v.clone())?
1577 }
1578 PXConfigKey::IpHeaders => self.ip_headers = serde_json::from_value(v.clone())?,
1579 PXConfigKey::LogEndpoint => self.log_endpoint = serde_json::from_value(v.clone())?,
1580 PXConfigKey::DataEnrichmentHeaderName => {
1581 self.data_enrichment_header_name = serde_json::from_value(v.clone())?
1582 }
1583 PXConfigKey::ExtractedCookies => {
1584 self.extracted_cookies = serde_json::from_value(v.clone())?
1585 }
1586 PXConfigKey::CorsSupportEnabled => {
1587 self.cors_support_enabled = serde_json::from_value(v.clone())?
1588 }
1589 PXConfigKey::CorsPreflightRequestFilterEnabled => {
1590 self.cors_preflight_request_filter_enabled = serde_json::from_value(v.clone())?
1591 }
1592 PXConfigKey::GraphqlEnabled => {
1593 self.graphql_enabled = serde_json::from_value(v.clone())?
1594 }
1595 PXConfigKey::GraphqlRoutes => {
1596 let pats: Vec<String> = serde_json::from_value(v.clone())?;
1597 self.graphql_routes = compile_regex_vec(pats, key.as_ref());
1598 }
1599 PXConfigKey::SensitiveGraphqlOperationNames => {
1600 self.sensitive_graphql_operation_names = serde_json::from_value(v.clone())?
1601 }
1602 PXConfigKey::SensitiveGraphqlOperationTypes => {
1603 self.sensitive_graphql_operation_types = serde_json::from_value(v.clone())?
1604 }
1605 PXConfigKey::GraphqlBodyMaxLength => {
1606 self.graphql_body_max_length = serde_json::from_value(v.clone())?
1607 }
1608 PXConfigKey::GraphqlKeywords => {
1609 let pats: Vec<String> = serde_json::from_value(v.clone())?;
1610 self.graphql_keywords = compile_regex_vec(pats, key.as_ref());
1611 }
1612 PXConfigKey::S2sTimeout => self.s2s_timeout = serde_json::from_value(v.clone())?,
1613 PXConfigKey::TokenVersion => self.token_version = serde_json::from_value(v.clone())?,
1614 PXConfigKey::CustomFirstPartyCaptchaEndpoint => {
1615 self.custom_first_party_captcha_endpoint = serde_json::from_value(v.clone())?
1616 }
1617 PXConfigKey::CustomFirstPartySensorEndpoint => {
1618 self.custom_first_party_sensor_endpoint = serde_json::from_value(v.clone())?
1619 }
1620 PXConfigKey::CustomFirstPartyXhrEndpoint => {
1621 self.custom_first_party_xhr_endpoint = serde_json::from_value(v.clone())?
1622 }
1623 PXConfigKey::UserAgentMaxLength => {
1624 self.user_agent_max_length = serde_json::from_value(v.clone())?
1625 }
1626 PXConfigKey::RiskCookieMaxLength => {
1627 self.risk_cookie_max_length = serde_json::from_value(v.clone())?
1628 }
1629 PXConfigKey::RiskCookieMinIterations => {
1630 self.risk_cookie_min_iterations = serde_json::from_value(v.clone())?
1631 }
1632 PXConfigKey::RiskCookieMaxIterations => {
1633 self.risk_cookie_max_iterations = serde_json::from_value(v.clone())?
1634 }
1635 PXConfigKey::AgenticTrustEnabled => {
1636 self.agentic_trust_enabled = serde_json::from_value(v.clone())?
1637 }
1638 PXConfigKey::AgenticTrustMcpEndpointPath => {
1639 self.agentic_trust_mcp_endpoint_path = serde_json::from_value(v.clone())?
1640 }
1641 PXConfigKey::SecuredPxhdEnabled => {
1642 self.secured_pxhd_enabled = serde_json::from_value(v.clone())?
1643 }
1644 PXConfigKey::PxhdDomain => self.pxhd_domain = serde_json::from_value(v.clone())?,
1645 PXConfigKey::JwtCookieName => self.jwt_cookie_name = serde_json::from_value(v.clone())?,
1646 PXConfigKey::JwtCookieUserIdFieldName => {
1647 self.jwt_cookie_user_id_field_name = serde_json::from_value(v.clone())?
1648 }
1649 PXConfigKey::JwtCookieAdditionalFieldNames => {
1650 self.jwt_cookie_additional_field_names = serde_json::from_value(v.clone())?
1651 }
1652 PXConfigKey::JwtHeaderName => self.jwt_header_name = serde_json::from_value(v.clone())?,
1653 PXConfigKey::JwtHeaderUserIdFieldName => {
1654 self.jwt_header_user_id_field_name = serde_json::from_value(v.clone())?
1655 }
1656 PXConfigKey::JwtHeaderAdditionalFieldNames => {
1657 self.jwt_header_additional_field_names = serde_json::from_value(v.clone())?
1658 }
1659 PXConfigKey::LoggerAuthToken => {
1660 self.logger_auth_token = serde_json::from_value(v.clone())?
1661 }
1662 PXConfigKey::LoginCredentialsExtractionEnabled => {
1663 self.login_credentials_extraction_enabled = serde_json::from_value(v.clone())?
1664 }
1665 PXConfigKey::LoginCredentialsExtraction => {
1666 self.login_credentials_extraction = parse_ci_endpoints_from_value(v)?;
1667 rebuild_prepared_ci_endpoints(self);
1668 }
1669 PXConfigKey::CredentialsIntelligenceVersion => {
1670 self.credentials_intelligence_version = serde_json::from_value(v.clone())?
1671 }
1672 PXConfigKey::CompromisedCredentialsHeader => {
1673 self.compromised_credentials_header = serde_json::from_value(v.clone())?
1674 }
1675 PXConfigKey::SendRawUsernameOnAdditionalS2sActivity => {
1676 self.send_raw_username_on_additional_s2s_activity =
1677 serde_json::from_value(v.clone())?
1678 }
1679 PXConfigKey::AdditionalS2sActivityEnabled => {
1680 self.additional_s2s_activity_enabled = serde_json::from_value(v.clone())?
1681 }
1682 PXConfigKey::AdditionalS2sActivityHeaderEnabled => {
1683 self.additional_s2s_activity_header_enabled = serde_json::from_value(v.clone())?
1684 }
1685 PXConfigKey::LoginSuccessfulReportingMethod => {
1686 self.login_successful_reporting_method = serde_json::from_value(v.clone())?
1687 }
1688 PXConfigKey::LoginSuccessfulBodyRegex => {
1689 self.login_successful_body_regex = serde_json::from_value(v.clone())?
1690 }
1691 PXConfigKey::LoginSuccessfulHeaderName => {
1692 self.login_successful_header_name = serde_json::from_value(v.clone())?
1693 }
1694 PXConfigKey::LoginSuccessfulHeaderValue => {
1695 self.login_successful_header_value = serde_json::from_value(v.clone())?
1696 }
1697 PXConfigKey::LoginSuccessfulStatus => {
1698 self.login_successful_status = serde_json::from_value(v.clone())?
1699 }
1700 }
1701 Ok(())
1702 }
1703
1704 pub(crate) fn set_from_raw(&mut self, key: PXConfigKey, raw: &str) {
1708 let raw = strip_json_quotes(raw);
1709 match key {
1710 PXConfigKey::AppId => self.app_id = raw.to_owned(),
1711 PXConfigKey::CookieSecret => self.cookie_secret = raw.to_owned(),
1712 PXConfigKey::AuthToken => self.auth_token = raw.to_owned(),
1713 PXConfigKey::Debug => self.debug = raw.parse().unwrap_or(self.debug),
1714 PXConfigKey::BlockingScore => {
1715 self.blocking_score = raw.parse().unwrap_or(self.blocking_score)
1716 }
1717 PXConfigKey::ModuleEnabled => {
1718 self.module_enabled = raw.parse().unwrap_or(self.module_enabled)
1719 }
1720 PXConfigKey::ModuleMode => self.module_mode = raw.parse().unwrap_or(self.module_mode),
1721 PXConfigKey::SensitiveHeaders => self.sensitive_headers = parse_vec(raw),
1722 PXConfigKey::SensitiveRoutes => self.sensitive_routes = parse_vec(raw),
1723 PXConfigKey::SensitiveRoutesRegex => {
1724 self.sensitive_routes_regex = parse_regex_vec(key.as_ref(), raw)
1725 }
1726 PXConfigKey::FilterByRoute => self.filter_by_route = parse_vec(raw),
1727 PXConfigKey::FilterByExtension => self.filter_by_extension = parse_vec(raw),
1728 PXConfigKey::FilterByUserAgent => self.filter_by_user_agent = parse_vec(raw),
1729 PXConfigKey::FilterByIp => self.filter_by_ip = parse_vec(raw),
1730 PXConfigKey::FilterByHttpMethod => self.filter_by_http_method = parse_vec(raw),
1731 PXConfigKey::CustomCookieHeader => self.custom_cookie_header = parse_string(raw),
1732 PXConfigKey::EnforcedRoutes => self.enforced_routes = parse_vec(raw),
1733 PXConfigKey::MonitoredRoutes => self.monitored_routes = parse_vec(raw),
1734 PXConfigKey::BypassMonitorHeader => self.bypass_monitor_header = parse_string(raw),
1735 PXConfigKey::FirstPartyEnabled => {
1736 self.first_party_enabled = raw.parse().unwrap_or(self.first_party_enabled)
1737 }
1738 PXConfigKey::CustomLogo => self.custom_logo = parse_string(raw),
1739 PXConfigKey::JsRef => self.js_ref = parse_string(raw),
1740 PXConfigKey::CssRef => self.css_ref = parse_string(raw),
1741 PXConfigKey::HumanSapiHost => self.human_sapi_host = parse_string(raw),
1742 PXConfigKey::HumanSapiBackend => self.human_sapi_backend = parse_string(raw),
1743 PXConfigKey::HumanCollectorHost => self.human_collector_host = parse_string(raw),
1744 PXConfigKey::HumanCollectorBackend => self.human_collector_backend = parse_string(raw),
1745 PXConfigKey::HumanClientHost => self.human_client_host = parse_string(raw),
1746 PXConfigKey::HumanClientBackend => self.human_client_backend = parse_string(raw),
1747 PXConfigKey::HumanCaptchaHost => self.human_captcha_host = parse_string(raw),
1748 PXConfigKey::HumanCaptchaBackend => self.human_captcha_backend = parse_string(raw),
1749 PXConfigKey::IpHeaders => self.ip_headers = parse_vec(raw),
1750 PXConfigKey::LogEndpoint => self.log_endpoint = raw.to_owned(),
1751 PXConfigKey::DataEnrichmentHeaderName => {
1752 self.data_enrichment_header_name = parse_string(raw)
1753 }
1754 PXConfigKey::ExtractedCookies => self.extracted_cookies = parse_vec(raw),
1755 PXConfigKey::CorsSupportEnabled => {
1756 self.cors_support_enabled = raw.parse().unwrap_or(self.cors_support_enabled)
1757 }
1758 PXConfigKey::CorsPreflightRequestFilterEnabled => {
1759 self.cors_preflight_request_filter_enabled = raw
1760 .parse()
1761 .unwrap_or(self.cors_preflight_request_filter_enabled)
1762 }
1763 PXConfigKey::GraphqlEnabled => {
1764 self.graphql_enabled = raw.parse().unwrap_or(self.graphql_enabled)
1765 }
1766 PXConfigKey::GraphqlRoutes => self.graphql_routes = parse_regex_vec(key.as_ref(), raw),
1767 PXConfigKey::SensitiveGraphqlOperationNames => {
1768 self.sensitive_graphql_operation_names = parse_vec(raw)
1769 }
1770 PXConfigKey::SensitiveGraphqlOperationTypes => {
1771 self.sensitive_graphql_operation_types = parse_vec(raw)
1772 }
1773 PXConfigKey::GraphqlBodyMaxLength => {
1774 self.graphql_body_max_length = raw.parse().unwrap_or(self.graphql_body_max_length)
1775 }
1776 PXConfigKey::GraphqlKeywords => {
1777 self.graphql_keywords = parse_regex_vec(key.as_ref(), raw)
1778 }
1779 PXConfigKey::S2sTimeout => self.s2s_timeout = raw.parse().unwrap_or(self.s2s_timeout),
1780 PXConfigKey::TokenVersion => {
1781 self.token_version = raw.parse().unwrap_or(self.token_version)
1782 }
1783 PXConfigKey::CustomFirstPartyCaptchaEndpoint => {
1784 self.custom_first_party_captcha_endpoint = parse_string(raw)
1785 }
1786 PXConfigKey::CustomFirstPartySensorEndpoint => {
1787 self.custom_first_party_sensor_endpoint = parse_string(raw)
1788 }
1789 PXConfigKey::CustomFirstPartyXhrEndpoint => {
1790 self.custom_first_party_xhr_endpoint = parse_string(raw)
1791 }
1792 PXConfigKey::UserAgentMaxLength => {
1793 self.user_agent_max_length = raw.parse().unwrap_or(self.user_agent_max_length)
1794 }
1795 PXConfigKey::RiskCookieMaxLength => {
1796 self.risk_cookie_max_length = raw.parse().unwrap_or(self.risk_cookie_max_length)
1797 }
1798 PXConfigKey::RiskCookieMinIterations => {
1799 self.risk_cookie_min_iterations =
1800 raw.parse().unwrap_or(self.risk_cookie_min_iterations)
1801 }
1802 PXConfigKey::RiskCookieMaxIterations => {
1803 self.risk_cookie_max_iterations =
1804 raw.parse().unwrap_or(self.risk_cookie_max_iterations)
1805 }
1806 PXConfigKey::AgenticTrustEnabled => {
1807 self.agentic_trust_enabled = raw.parse().unwrap_or(self.agentic_trust_enabled)
1808 }
1809 PXConfigKey::AgenticTrustMcpEndpointPath => {
1810 self.agentic_trust_mcp_endpoint_path = parse_string(raw)
1811 }
1812 PXConfigKey::SecuredPxhdEnabled => {
1813 self.secured_pxhd_enabled = raw.parse().unwrap_or(self.secured_pxhd_enabled)
1814 }
1815 PXConfigKey::PxhdDomain => self.pxhd_domain = parse_string(raw),
1816 PXConfigKey::JwtCookieName => self.jwt_cookie_name = parse_string(raw),
1817 PXConfigKey::JwtCookieUserIdFieldName => {
1818 self.jwt_cookie_user_id_field_name = parse_string(raw)
1819 }
1820 PXConfigKey::JwtCookieAdditionalFieldNames => {
1821 self.jwt_cookie_additional_field_names = parse_vec(raw)
1822 }
1823 PXConfigKey::JwtHeaderName => self.jwt_header_name = parse_string(raw),
1824 PXConfigKey::JwtHeaderUserIdFieldName => {
1825 self.jwt_header_user_id_field_name = parse_string(raw)
1826 }
1827 PXConfigKey::JwtHeaderAdditionalFieldNames => {
1828 self.jwt_header_additional_field_names = parse_vec(raw)
1829 }
1830 PXConfigKey::LoggerAuthToken => self.logger_auth_token = raw.to_owned(),
1831 PXConfigKey::LoginCredentialsExtractionEnabled => {
1832 self.login_credentials_extraction_enabled = raw
1833 .parse()
1834 .unwrap_or(self.login_credentials_extraction_enabled)
1835 }
1836 PXConfigKey::LoginCredentialsExtraction => {
1837 self.login_credentials_extraction = parse_ci_endpoints(raw);
1838 rebuild_prepared_ci_endpoints(self);
1839 }
1840 PXConfigKey::CredentialsIntelligenceVersion => {
1841 self.credentials_intelligence_version = parse_string(raw)
1842 }
1843 PXConfigKey::CompromisedCredentialsHeader => {
1844 self.compromised_credentials_header = parse_string(raw)
1845 }
1846 PXConfigKey::SendRawUsernameOnAdditionalS2sActivity => {
1847 self.send_raw_username_on_additional_s2s_activity = raw
1848 .parse()
1849 .unwrap_or(self.send_raw_username_on_additional_s2s_activity)
1850 }
1851 PXConfigKey::AdditionalS2sActivityEnabled => {
1852 self.additional_s2s_activity_enabled =
1853 raw.parse().unwrap_or(self.additional_s2s_activity_enabled)
1854 }
1855 PXConfigKey::AdditionalS2sActivityHeaderEnabled => {
1856 self.additional_s2s_activity_header_enabled = raw
1857 .parse()
1858 .unwrap_or(self.additional_s2s_activity_header_enabled)
1859 }
1860 PXConfigKey::LoginSuccessfulReportingMethod => {
1861 self.login_successful_reporting_method = parse_string(raw)
1862 }
1863 PXConfigKey::LoginSuccessfulBodyRegex => {
1864 self.login_successful_body_regex = parse_string(raw)
1865 }
1866 PXConfigKey::LoginSuccessfulHeaderName => {
1867 self.login_successful_header_name = parse_string(raw)
1868 }
1869 PXConfigKey::LoginSuccessfulHeaderValue => {
1870 self.login_successful_header_value = parse_string(raw)
1871 }
1872 PXConfigKey::LoginSuccessfulStatus => {
1873 self.login_successful_status = parse_status_vec(raw)
1874 }
1875 }
1876 }
1877
1878 pub fn set_ci_extract_credentials_fn(&mut self, f: PXExtractCredentialsFn) {
1880 self.ci_extract_credentials_fn = Some(f);
1881 }
1882
1883 pub fn set_ci_login_successful_fn(&mut self, f: PXLoginSuccessfulFn) {
1885 self.ci_login_successful_fn = Some(f);
1886 }
1887}