File size: 5,555 Bytes
8ede856
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
"""Tests for security fixes - cryptographic random number generation and SSL context."""

import os
import ssl
import sys

# Add project root to sys.path
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))

import pytest


def test_wecom_crypto_uses_secrets():
    """Test that WXBizJsonMsgCrypt uses secrets module instead of random."""
    from astrbot.core.platform.sources.wecom_ai_bot.WXBizJsonMsgCrypt import Prpcrypt

    # Create an instance and test that random string generation works
    prpcrypt = Prpcrypt(b"test_key_32_bytes_long_value!")

    # Generate multiple random strings and verify they are different and valid
    random_strings = [prpcrypt.get_random_str() for _ in range(10)]

    # All strings should be 16 bytes long
    assert all(len(s) == 16 for s in random_strings)

    # All strings should be different (extremely high probability with cryptographic random)
    assert len(set(random_strings)) == 10

    # All strings should be numeric when decoded
    for s in random_strings:
        decoded = s.decode()
        assert decoded.isdigit()
        assert 1000000000000000 <= int(decoded) <= 9999999999999999


def test_wecomai_utils_uses_secrets():
    """Test that wecomai_utils uses secrets module for random string generation."""
    from astrbot.core.platform.sources.wecom_ai_bot.wecomai_utils import (
        generate_random_string,
    )

    # Generate multiple random strings and verify they are different
    random_strings = [generate_random_string(10) for _ in range(20)]

    # All strings should be 10 characters long
    assert all(len(s) == 10 for s in random_strings)

    # All strings should be alphanumeric
    for s in random_strings:
        assert s.isalnum()

    # All strings should be different (extremely high probability with cryptographic random)
    assert len(set(random_strings)) >= 19  # Allow for 1 collision in 20 (very unlikely)


def test_azure_tts_signature_uses_secrets():
    """Test that Azure TTS signature generation uses secrets module."""
    import asyncio

    from astrbot.core.provider.sources.azure_tts_source import OTTSProvider

    # Create a provider with test config
    config = {
        "OTTS_SKEY": "test_secret_key",
        "OTTS_URL": "https://example.com/api/tts",
        "OTTS_AUTH_TIME": "https://example.com/api/time",
    }

    async def test_nonce_generation():
        async with OTTSProvider(config) as provider:
            # Mock time sync to avoid actual API calls
            provider.time_offset = 0
            provider.last_sync_time = 9999999999

            # Generate multiple signatures and extract nonces
            signatures = []
            for _ in range(10):
                sig = await provider._generate_signature()
                signatures.append(sig)

            # Extract nonces (second field in signature format: timestamp-nonce-0-hash)
            nonces = [sig.split("-")[1] for sig in signatures]

            # All nonces should be 10 characters long
            assert all(len(n) == 10 for n in nonces)

            # All nonces should be alphanumeric (lowercase letters and digits)
            for n in nonces:
                assert all(c in "abcdefghijklmnopqrstuvwxyz0123456789" for c in n)

            # All nonces should be different (cryptographic random ensures uniqueness)
            assert len(set(nonces)) == 10

    asyncio.run(test_nonce_generation())


def test_ssl_context_fallback_explicit():
    """Test that SSL context fallback is properly configured."""
    # This test verifies the SSL context configuration
    # We can't easily test the full io.py functions without network calls,
    # but we can verify that ssl.CERT_NONE and check_hostname=False are valid settings

    # Create a context similar to what's used in io.py
    ssl_context = ssl.create_default_context()
    ssl_context.check_hostname = False
    ssl_context.verify_mode = ssl.CERT_NONE

    # Verify the settings are applied correctly
    assert ssl_context.check_hostname is False
    assert ssl_context.verify_mode == ssl.CERT_NONE

    # This configuration should work but is intentionally insecure for fallback
    # The actual code only uses this when certificate validation fails


def test_io_module_has_ssl_imports():
    """Verify that io.py properly imports ssl module."""
    from astrbot.core.utils import io

    # Check that ssl is available in the module
    assert hasattr(io, "ssl")

    # Check that CERT_NONE constant is accessible
    assert hasattr(io.ssl, "CERT_NONE")


def test_secrets_module_randomness_quality():
    """Test that secrets module provides high-quality randomness."""
    import secrets

    # Generate a large set of random numbers
    random_numbers = [secrets.randbelow(100) for _ in range(1000)]

    # Basic statistical test: should have good distribution
    unique_values = len(set(random_numbers))

    # With 1000 random numbers from 0-99, we should see most values at least once
    # This is a very basic test - real cryptographic random should pass this easily
    assert unique_values >= 60  # Should see at least 60 different values out of 100

    # Test secrets.choice for string generation
    chars = "abcdefghijklmnopqrstuvwxyz0123456789"
    random_chars = [secrets.choice(chars) for _ in range(1000)]

    # Should have good character distribution
    unique_chars = len(set(random_chars))
    assert unique_chars >= 20  # Should see at least 20 different characters


if __name__ == "__main__":
    pytest.main([__file__, "-v"])