1use anyhow::Result;
8use serde::Serialize;
9use std::collections::{HashMap, HashSet, VecDeque};
10use std::net::IpAddr;
11use std::sync::Arc;
12use std::time::{Duration, Instant, SystemTime};
13use tokio::sync::{Mutex as AsyncMutex, RwLock as AsyncRwLock};
14use tracing::instrument;
15
16use crate::ast::OperationType;
17
18#[derive(Debug, Clone)]
20pub struct SecurityConfig {
21 pub enable_rate_limiting: bool,
23 pub enable_query_depth_analysis: bool,
24 pub enable_query_complexity_analysis: bool,
25 pub enable_query_whitelisting: bool,
26 pub enable_field_authorization: bool,
27 pub enable_introspection_protection: bool,
28 pub enable_mutation_protection: bool,
29 pub enable_subscription_protection: bool,
30 pub enable_ip_filtering: bool,
31 pub enable_user_agent_filtering: bool,
32 pub enable_threat_detection: bool,
33 pub enable_audit_logging: bool,
34
35 pub rate_limit_requests_per_minute: usize,
37 pub rate_limit_burst_capacity: usize,
38 pub rate_limit_window_size: Duration,
39
40 pub max_query_depth: usize,
42 pub max_query_complexity: usize,
43 pub max_query_aliases: usize,
44 pub max_query_fields: usize,
45 pub max_selection_sets: usize,
46
47 pub require_authentication: bool,
49 pub jwt_secret: Option<String>,
50 pub api_key_header: String,
51 pub session_timeout: Duration,
52
53 pub threat_detection_sensitivity: ThreatSensitivity,
55 pub max_failed_attempts: usize,
56 pub lockout_duration: Duration,
57 pub suspicious_pattern_threshold: f64,
58
59 pub allowed_ip_ranges: Vec<IpRange>,
61 pub blocked_ips: HashSet<IpAddr>,
62 pub geolocation_restrictions: Option<GeolocationRestrictions>,
63}
64
65impl Default for SecurityConfig {
66 fn default() -> Self {
67 Self {
68 enable_rate_limiting: true,
69 enable_query_depth_analysis: true,
70 enable_query_complexity_analysis: true,
71 enable_query_whitelisting: false,
72 enable_field_authorization: true,
73 enable_introspection_protection: true,
74 enable_mutation_protection: true,
75 enable_subscription_protection: true,
76 enable_ip_filtering: false,
77 enable_user_agent_filtering: false,
78 enable_threat_detection: true,
79 enable_audit_logging: true,
80
81 rate_limit_requests_per_minute: 100,
82 rate_limit_burst_capacity: 20,
83 rate_limit_window_size: Duration::from_secs(60),
84
85 max_query_depth: 10,
86 max_query_complexity: 1000,
87 max_query_aliases: 50,
88 max_query_fields: 100,
89 max_selection_sets: 50,
90
91 require_authentication: false,
92 jwt_secret: None,
93 api_key_header: "X-API-Key".to_string(),
94 session_timeout: Duration::from_secs(3600),
95
96 threat_detection_sensitivity: ThreatSensitivity::Medium,
97 max_failed_attempts: 5,
98 lockout_duration: Duration::from_secs(300),
99 suspicious_pattern_threshold: 0.8,
100
101 allowed_ip_ranges: Vec::new(),
102 blocked_ips: HashSet::new(),
103 geolocation_restrictions: None,
104 }
105 }
106}
107
108#[derive(Debug, Clone, Copy)]
110pub enum ThreatSensitivity {
111 Low,
112 Medium,
113 High,
114 Paranoid,
115}
116
117#[derive(Debug, Clone)]
119pub struct IpRange {
120 pub network: IpAddr,
121 pub prefix_length: u8,
122}
123
124#[derive(Debug, Clone)]
126pub struct GeolocationRestrictions {
127 pub allowed_countries: HashSet<String>,
128 pub blocked_countries: HashSet<String>,
129 pub allowed_regions: HashSet<String>,
130 pub blocked_regions: HashSet<String>,
131}
132
133#[derive(Debug, Clone)]
135pub struct SecurityContext {
136 pub client_ip: IpAddr,
137 pub user_agent: Option<String>,
138 pub authentication_token: Option<String>,
139 pub user_id: Option<String>,
140 pub permissions: HashSet<String>,
141 pub roles: HashSet<String>,
142 pub session_id: String,
143 pub request_timestamp: SystemTime,
144 pub geographical_info: Option<GeographicalInfo>,
145}
146
147#[derive(Debug, Clone)]
149pub struct GeographicalInfo {
150 pub country: String,
151 pub region: String,
152 pub city: String,
153 pub latitude: f64,
154 pub longitude: f64,
155 pub timezone: String,
156}
157
158#[derive(Debug, Clone, Serialize)]
160pub enum SecurityViolation {
161 RateLimitExceeded {
162 requests_per_minute: usize,
163 limit: usize,
164 },
165 QueryDepthExceeded {
166 depth: usize,
167 max_depth: usize,
168 },
169 QueryComplexityExceeded {
170 complexity: usize,
171 max_complexity: usize,
172 },
173 UnauthorizedFieldAccess {
174 field_name: String,
175 required_permission: String,
176 },
177 IntrospectionBlocked,
178 MutationBlocked {
179 mutation_name: String,
180 },
181 SubscriptionBlocked {
182 subscription_name: String,
183 },
184 IpBlocked {
185 ip: IpAddr,
186 },
187 GeolocationBlocked {
188 country: String,
189 region: String,
190 },
191 SuspiciousActivity {
192 threat_score: f64,
193 patterns: Vec<String>,
194 },
195 AuthenticationRequired,
196 InvalidToken,
197 InsufficientPermissions {
198 required: String,
199 available: Vec<String>,
200 },
201 QueryNotWhitelisted {
202 query_signature: String,
203 },
204}
205
206#[derive(Debug, Clone, Serialize)]
208pub struct AuditLogEntry {
209 pub timestamp: SystemTime,
210 pub client_ip: IpAddr,
211 pub user_id: Option<String>,
212 pub session_id: String,
213 pub operation_type: OperationType,
214 pub query: String,
215 pub variables: Option<serde_json::Value>,
216 pub execution_time: Duration,
217 pub success: bool,
218 pub violations: Vec<SecurityViolation>,
219 pub response_size: usize,
220 pub user_agent: Option<String>,
221}
222
223#[derive(Debug)]
225struct RateLimitState {
226 requests: VecDeque<Instant>,
227 blocked_until: Option<Instant>,
228 total_requests: usize,
229 #[allow(dead_code)]
230 first_request: Instant,
231}
232
233#[derive(Debug)]
235struct ClientSecurityState {
236 #[allow(dead_code)]
237 failed_attempts: usize,
238 #[allow(dead_code)]
239 locked_until: Option<Instant>,
240 #[allow(dead_code)]
241 threat_score: f64,
242 #[allow(dead_code)]
243 suspicious_patterns: Vec<String>,
244 #[allow(dead_code)]
245 first_seen: SystemTime,
246 #[allow(dead_code)]
247 last_activity: SystemTime,
248}
249
250#[derive(Debug)]
252pub struct QueryAnalysisResult {
253 pub depth: usize,
254 pub complexity: usize,
255 pub field_count: usize,
256 pub alias_count: usize,
257 pub selection_set_count: usize,
258 pub contains_introspection: bool,
259 pub mutations: Vec<String>,
260 pub subscriptions: Vec<String>,
261 pub required_permissions: HashSet<String>,
262 pub query_signature: String,
263}
264
265pub struct AdvancedSecuritySystem {
267 config: SecurityConfig,
268 rate_limits: Arc<AsyncMutex<HashMap<IpAddr, RateLimitState>>>,
269 #[allow(dead_code)]
270 client_states: Arc<AsyncMutex<HashMap<IpAddr, ClientSecurityState>>>,
271 #[allow(dead_code)]
272 whitelisted_queries: Arc<AsyncRwLock<HashSet<String>>>,
273 #[allow(dead_code)]
274 field_permissions: Arc<AsyncRwLock<HashMap<String, HashSet<String>>>>,
275 audit_log: Arc<AsyncMutex<VecDeque<AuditLogEntry>>>,
276 #[allow(dead_code)]
277 threat_detector: Arc<AsyncMutex<ThreatDetector>>,
278 #[allow(dead_code)]
279 auth_validator: Arc<AuthenticationValidator>,
280}
281
282#[derive(Debug)]
284pub struct ThreatDetector {
285 #[allow(dead_code)]
286 known_attack_patterns: Vec<AttackPattern>,
287 #[allow(dead_code)]
288 behavioral_baselines: HashMap<String, BehavioralBaseline>,
289 #[allow(dead_code)]
290 anomaly_threshold: f64,
291}
292
293#[derive(Debug, Clone)]
295pub struct AttackPattern {
296 pub name: String,
297 pub pattern_type: AttackPatternType,
298 pub signature: String,
299 pub severity: ThreatSeverity,
300 pub detection_rules: Vec<DetectionRule>,
301}
302
303#[derive(Debug, Clone)]
305pub enum AttackPatternType {
306 QueryComplexityAttack,
307 BatchQueryAttack,
308 IntrospectionAbuse,
309 FieldEnumeration,
310 DataExfiltration,
311 DenialOfService,
312 AuthenticationBypass,
313 PrivilegeEscalation,
314}
315
316#[derive(Debug, Clone)]
318pub enum ThreatSeverity {
319 Low,
320 Medium,
321 High,
322 Critical,
323}
324
325#[derive(Debug, Clone)]
327pub struct DetectionRule {
328 pub rule_type: DetectionRuleType,
329 pub threshold: f64,
330 pub time_window: Duration,
331}
332
333#[derive(Debug, Clone)]
335pub enum DetectionRuleType {
336 RequestFrequency,
337 QueryComplexity,
338 ErrorRate,
339 ResponseSize,
340 FieldAccessPattern,
341 UserAgentPattern,
342}
343
344#[derive(Debug, Clone)]
346pub struct BehavioralBaseline {
347 pub avg_query_complexity: f64,
348 pub avg_request_frequency: f64,
349 pub common_fields: HashSet<String>,
350 pub typical_operations: HashSet<OperationType>,
351 pub normal_response_sizes: (usize, usize), }
353
354#[derive(Debug)]
356pub struct AuthenticationValidator {
357 #[allow(dead_code)]
358 jwt_secret: Option<String>,
359 #[allow(dead_code)]
360 valid_sessions: Arc<AsyncRwLock<HashMap<String, SessionInfo>>>,
361 #[allow(dead_code)]
362 api_keys: Arc<AsyncRwLock<HashMap<String, ApiKeyInfo>>>,
363}
364
365#[derive(Debug, Clone)]
367pub struct SessionInfo {
368 pub user_id: String,
369 pub permissions: HashSet<String>,
370 pub roles: HashSet<String>,
371 pub created_at: SystemTime,
372 pub last_accessed: SystemTime,
373 pub expires_at: SystemTime,
374}
375
376#[derive(Debug, Clone)]
378pub struct ApiKeyInfo {
379 pub key_id: String,
380 pub permissions: HashSet<String>,
381 pub rate_limit: Option<usize>,
382 pub created_at: SystemTime,
383 pub last_used: SystemTime,
384 pub expires_at: Option<SystemTime>,
385}
386
387impl AdvancedSecuritySystem {
388 pub fn new(config: SecurityConfig) -> Self {
390 let auth_validator = Arc::new(AuthenticationValidator {
391 jwt_secret: config.jwt_secret.clone(),
392 valid_sessions: Arc::new(AsyncRwLock::new(HashMap::new())),
393 api_keys: Arc::new(AsyncRwLock::new(HashMap::new())),
394 });
395
396 let threat_detector = ThreatDetector {
397 known_attack_patterns: Self::initialize_attack_patterns(),
398 behavioral_baselines: HashMap::new(),
399 anomaly_threshold: match config.threat_detection_sensitivity {
400 ThreatSensitivity::Low => 0.9,
401 ThreatSensitivity::Medium => 0.8,
402 ThreatSensitivity::High => 0.7,
403 ThreatSensitivity::Paranoid => 0.5,
404 },
405 };
406
407 Self {
408 config,
409 rate_limits: Arc::new(AsyncMutex::new(HashMap::new())),
410 client_states: Arc::new(AsyncMutex::new(HashMap::new())),
411 whitelisted_queries: Arc::new(AsyncRwLock::new(HashSet::new())),
412 field_permissions: Arc::new(AsyncRwLock::new(HashMap::new())),
413 audit_log: Arc::new(AsyncMutex::new(VecDeque::new())),
414 threat_detector: Arc::new(AsyncMutex::new(threat_detector)),
415 auth_validator,
416 }
417 }
418
419 #[instrument(skip(self, query))]
421 pub async fn validate_request(
422 &self,
423 context: &SecurityContext,
424 query: &str,
425 _variables: Option<&serde_json::Value>,
426 operation_name: Option<&str>,
427 ) -> Result<Vec<SecurityViolation>> {
428 let mut violations = Vec::new();
429
430 if self.config.enable_ip_filtering {
432 if let Some(violation) = self.check_ip_filtering(&context.client_ip).await? {
433 violations.push(violation);
434 return Ok(violations); }
436 }
437
438 if self.config.enable_rate_limiting {
440 if let Some(violation) = self.check_rate_limiting(&context.client_ip).await? {
441 violations.push(violation);
442 }
443 }
444
445 if self.config.require_authentication {
447 if let Some(violation) = self.check_authentication(context).await? {
448 violations.push(violation);
449 return Ok(violations); }
451 }
452
453 let analysis = self.analyze_query(query).await?;
455
456 if self.config.enable_query_depth_analysis {
458 if let Some(violation) = self.check_query_depth(&analysis) {
459 violations.push(violation);
460 }
461 }
462
463 if self.config.enable_query_complexity_analysis {
465 if let Some(violation) = self.check_query_complexity(&analysis) {
466 violations.push(violation);
467 }
468 }
469
470 if self.config.enable_query_whitelisting {
472 if let Some(violation) = self.check_query_whitelist(&analysis).await? {
473 violations.push(violation);
474 }
475 }
476
477 if self.config.enable_field_authorization {
479 let field_violations = self.check_field_authorization(context, &analysis).await?;
480 violations.extend(field_violations);
481 }
482
483 if self.config.enable_introspection_protection && analysis.contains_introspection {
485 violations.push(SecurityViolation::IntrospectionBlocked);
486 }
487
488 if self.config.enable_mutation_protection {
490 for mutation in &analysis.mutations {
491 if !self.is_mutation_allowed(context, mutation).await? {
492 violations.push(SecurityViolation::MutationBlocked {
493 mutation_name: mutation.clone(),
494 });
495 }
496 }
497 }
498
499 if self.config.enable_subscription_protection {
501 for subscription in &analysis.subscriptions {
502 if !self.is_subscription_allowed(context, subscription).await? {
503 violations.push(SecurityViolation::SubscriptionBlocked {
504 subscription_name: subscription.clone(),
505 });
506 }
507 }
508 }
509
510 if self.config.enable_threat_detection {
512 if let Some(violation) = self.detect_threats(context, &analysis).await? {
513 violations.push(violation);
514 }
515 }
516
517 Ok(violations)
518 }
519
520 async fn analyze_query(&self, query: &str) -> Result<QueryAnalysisResult> {
522 let depth = self.calculate_query_depth(query);
526 let complexity = self.calculate_query_complexity(query);
527 let field_count = self.count_fields(query);
528 let alias_count = self.count_aliases(query);
529 let selection_set_count = self.count_selection_sets(query);
530 let contains_introspection = query.contains("__schema") || query.contains("__type");
531 let mutations = self.extract_mutations(query);
532 let subscriptions = self.extract_subscriptions(query);
533 let required_permissions = self.extract_required_permissions(query).await?;
534 let query_signature = self.generate_query_signature(query);
535
536 Ok(QueryAnalysisResult {
537 depth,
538 complexity,
539 field_count,
540 alias_count,
541 selection_set_count,
542 contains_introspection,
543 mutations,
544 subscriptions,
545 required_permissions,
546 query_signature,
547 })
548 }
549
550 async fn check_ip_filtering(&self, client_ip: &IpAddr) -> Result<Option<SecurityViolation>> {
552 if self.config.blocked_ips.contains(client_ip) {
553 return Ok(Some(SecurityViolation::IpBlocked { ip: *client_ip }));
554 }
555
556 if !self.config.allowed_ip_ranges.is_empty() {
558 let mut allowed = false;
559 for range in &self.config.allowed_ip_ranges {
560 if self.ip_in_range(client_ip, range) {
561 allowed = true;
562 break;
563 }
564 }
565 if !allowed {
566 return Ok(Some(SecurityViolation::IpBlocked { ip: *client_ip }));
567 }
568 }
569
570 Ok(None)
571 }
572
573 async fn check_rate_limiting(&self, client_ip: &IpAddr) -> Result<Option<SecurityViolation>> {
575 let mut rate_limits = self.rate_limits.lock().await;
576 let now = Instant::now();
577
578 let rate_limit_state = rate_limits
579 .entry(*client_ip)
580 .or_insert_with(|| RateLimitState {
581 requests: VecDeque::new(),
582 blocked_until: None,
583 total_requests: 0,
584 first_request: now,
585 });
586
587 if let Some(blocked_until) = rate_limit_state.blocked_until {
589 if now < blocked_until {
590 return Ok(Some(SecurityViolation::RateLimitExceeded {
591 requests_per_minute: rate_limit_state.requests.len(),
592 limit: self.config.rate_limit_requests_per_minute,
593 }));
594 } else {
595 rate_limit_state.blocked_until = None;
596 }
597 }
598
599 let window_start = now - self.config.rate_limit_window_size;
601 while let Some(&front_time) = rate_limit_state.requests.front() {
602 if front_time < window_start {
603 rate_limit_state.requests.pop_front();
604 } else {
605 break;
606 }
607 }
608
609 if rate_limit_state.requests.len() >= self.config.rate_limit_requests_per_minute {
611 rate_limit_state.blocked_until = Some(now + self.config.rate_limit_window_size);
612 return Ok(Some(SecurityViolation::RateLimitExceeded {
613 requests_per_minute: rate_limit_state.requests.len(),
614 limit: self.config.rate_limit_requests_per_minute,
615 }));
616 }
617
618 rate_limit_state.requests.push_back(now);
620 rate_limit_state.total_requests += 1;
621
622 Ok(None)
623 }
624
625 #[allow(clippy::too_many_arguments)]
627 #[instrument(skip(self, context, query, variables))]
628 pub async fn log_audit_event(
629 &self,
630 context: &SecurityContext,
631 operation_type: OperationType,
632 query: &str,
633 variables: Option<&serde_json::Value>,
634 execution_time: Duration,
635 success: bool,
636 violations: Vec<SecurityViolation>,
637 response_size: usize,
638 ) -> Result<()> {
639 if !self.config.enable_audit_logging {
640 return Ok(());
641 }
642
643 let entry = AuditLogEntry {
644 timestamp: SystemTime::now(),
645 client_ip: context.client_ip,
646 user_id: context.user_id.clone(),
647 session_id: context.session_id.clone(),
648 operation_type,
649 query: query.to_string(),
650 variables: variables.cloned(),
651 execution_time,
652 success,
653 violations,
654 response_size,
655 user_agent: context.user_agent.clone(),
656 };
657
658 let mut audit_log = self.audit_log.lock().await;
659 audit_log.push_back(entry);
660
661 while audit_log.len() > 10000 {
663 audit_log.pop_front();
664 }
665
666 Ok(())
667 }
668
669 fn initialize_attack_patterns() -> Vec<AttackPattern> {
671 vec![
672 AttackPattern {
673 name: "Deep Query Attack".to_string(),
674 pattern_type: AttackPatternType::QueryComplexityAttack,
675 signature: "excessive_depth".to_string(),
676 severity: ThreatSeverity::High,
677 detection_rules: vec![DetectionRule {
678 rule_type: DetectionRuleType::QueryComplexity,
679 threshold: 1000.0,
680 time_window: Duration::from_secs(60),
681 }],
682 },
683 AttackPattern {
684 name: "Introspection Abuse".to_string(),
685 pattern_type: AttackPatternType::IntrospectionAbuse,
686 signature: "introspection_query".to_string(),
687 severity: ThreatSeverity::Medium,
688 detection_rules: vec![DetectionRule {
689 rule_type: DetectionRuleType::FieldAccessPattern,
690 threshold: 5.0,
691 time_window: Duration::from_secs(300),
692 }],
693 },
694 ]
696 }
697
698 fn calculate_query_depth(&self, _query: &str) -> usize {
700 5
701 }
702 fn calculate_query_complexity(&self, _query: &str) -> usize {
703 100
704 }
705 fn count_fields(&self, _query: &str) -> usize {
706 10
707 }
708 fn count_aliases(&self, _query: &str) -> usize {
709 2
710 }
711 fn count_selection_sets(&self, _query: &str) -> usize {
712 3
713 }
714 fn extract_mutations(&self, _query: &str) -> Vec<String> {
715 Vec::new()
716 }
717 fn extract_subscriptions(&self, _query: &str) -> Vec<String> {
718 Vec::new()
719 }
720 async fn extract_required_permissions(&self, _query: &str) -> Result<HashSet<String>> {
721 Ok(HashSet::new())
722 }
723 fn generate_query_signature(&self, query: &str) -> String {
724 format!("sig_{}", query.len()) }
726 fn ip_in_range(&self, _ip: &IpAddr, _range: &IpRange) -> bool {
727 true
728 }
729 async fn check_authentication(
730 &self,
731 _context: &SecurityContext,
732 ) -> Result<Option<SecurityViolation>> {
733 Ok(None)
734 }
735 fn check_query_depth(&self, analysis: &QueryAnalysisResult) -> Option<SecurityViolation> {
736 if analysis.depth > self.config.max_query_depth {
737 Some(SecurityViolation::QueryDepthExceeded {
738 depth: analysis.depth,
739 max_depth: self.config.max_query_depth,
740 })
741 } else {
742 None
743 }
744 }
745 fn check_query_complexity(&self, analysis: &QueryAnalysisResult) -> Option<SecurityViolation> {
746 if analysis.complexity > self.config.max_query_complexity {
747 Some(SecurityViolation::QueryComplexityExceeded {
748 complexity: analysis.complexity,
749 max_complexity: self.config.max_query_complexity,
750 })
751 } else {
752 None
753 }
754 }
755 async fn check_query_whitelist(
756 &self,
757 _analysis: &QueryAnalysisResult,
758 ) -> Result<Option<SecurityViolation>> {
759 Ok(None)
760 }
761 async fn check_field_authorization(
762 &self,
763 _context: &SecurityContext,
764 _analysis: &QueryAnalysisResult,
765 ) -> Result<Vec<SecurityViolation>> {
766 Ok(Vec::new())
767 }
768 async fn is_mutation_allowed(
769 &self,
770 _context: &SecurityContext,
771 _mutation: &str,
772 ) -> Result<bool> {
773 Ok(true)
774 }
775 async fn is_subscription_allowed(
776 &self,
777 _context: &SecurityContext,
778 _subscription: &str,
779 ) -> Result<bool> {
780 Ok(true)
781 }
782 async fn detect_threats(
783 &self,
784 _context: &SecurityContext,
785 _analysis: &QueryAnalysisResult,
786 ) -> Result<Option<SecurityViolation>> {
787 Ok(None)
788 }
789}
790
791#[cfg(test)]
792mod tests {
793 use super::*;
794 use std::net::Ipv4Addr;
795
796 #[tokio::test]
797 async fn test_security_system_creation() {
798 let config = SecurityConfig::default();
799 let security_system = AdvancedSecuritySystem::new(config);
800
801 assert!(security_system.config.enable_rate_limiting);
802 assert!(security_system.config.enable_query_depth_analysis);
803 }
804
805 #[tokio::test]
806 async fn test_rate_limiting() {
807 let config = SecurityConfig {
808 rate_limit_requests_per_minute: 2,
809 ..Default::default()
810 };
811 let security_system = AdvancedSecuritySystem::new(config);
812
813 let ip = IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1));
814
815 let violation1 = security_system
817 .check_rate_limiting(&ip)
818 .await
819 .expect("should succeed");
820 assert!(violation1.is_none());
821
822 let violation2 = security_system
824 .check_rate_limiting(&ip)
825 .await
826 .expect("should succeed");
827 assert!(violation2.is_none());
828
829 let violation3 = security_system
831 .check_rate_limiting(&ip)
832 .await
833 .expect("should succeed");
834 assert!(violation3.is_some());
835
836 if let Some(SecurityViolation::RateLimitExceeded {
837 requests_per_minute,
838 limit,
839 }) = violation3
840 {
841 assert_eq!(requests_per_minute, 2);
842 assert_eq!(limit, 2);
843 } else {
844 panic!("Expected rate limit violation");
845 }
846 }
847
848 #[tokio::test]
849 async fn test_query_analysis() {
850 let config = SecurityConfig::default();
851 let security_system = AdvancedSecuritySystem::new(config);
852
853 let query = "query { user { name email } }";
854 let analysis = security_system
855 .analyze_query(query)
856 .await
857 .expect("should succeed");
858
859 assert!(analysis.depth > 0);
860 assert!(analysis.complexity > 0);
861 assert!(analysis.field_count > 0);
862 }
863}