update
Some checks failed
CI/CD Pipeline / Code Quality Checks (push) Failing after 4m49s
CI/CD Pipeline / Security Scanning (push) Successful in 15s
CI/CD Pipeline / Tests (3.11) (push) Successful in 9m41s
CI/CD Pipeline / Tests (3.12) (push) Successful in 9m36s
CI/CD Pipeline / Build Docker Image (push) Has been skipped
Dependency Updates / Update Dependencies (push) Successful in 29s
Some checks failed
CI/CD Pipeline / Code Quality Checks (push) Failing after 4m49s
CI/CD Pipeline / Security Scanning (push) Successful in 15s
CI/CD Pipeline / Tests (3.11) (push) Successful in 9m41s
CI/CD Pipeline / Tests (3.12) (push) Successful in 9m36s
CI/CD Pipeline / Build Docker Image (push) Has been skipped
Dependency Updates / Update Dependencies (push) Successful in 29s
This commit is contained in:
@@ -3,7 +3,8 @@
|
||||
import pytest
|
||||
from pydantic import ValidationError
|
||||
|
||||
from guardden.config import Settings, _parse_id_list, _validate_discord_id, normalize_domain
|
||||
from guardden.config import Settings, _parse_id_list, _validate_discord_id
|
||||
from guardden.services.automod import normalize_domain
|
||||
|
||||
|
||||
class TestDiscordIdValidation:
|
||||
@@ -17,7 +18,7 @@ class TestDiscordIdValidation:
|
||||
"1234567890123456789", # 19 digits
|
||||
123456789012345678, # int format
|
||||
]
|
||||
|
||||
|
||||
for valid_id in valid_ids:
|
||||
result = _validate_discord_id(valid_id)
|
||||
assert isinstance(result, int)
|
||||
@@ -35,7 +36,7 @@ class TestDiscordIdValidation:
|
||||
"0", # zero
|
||||
"-123456789012345678", # negative
|
||||
]
|
||||
|
||||
|
||||
for invalid_id in invalid_ids:
|
||||
with pytest.raises(ValueError):
|
||||
_validate_discord_id(invalid_id)
|
||||
@@ -45,7 +46,7 @@ class TestDiscordIdValidation:
|
||||
# Too small (before Discord existed)
|
||||
with pytest.raises(ValueError):
|
||||
_validate_discord_id("99999999999999999")
|
||||
|
||||
|
||||
# Too large (exceeds 64-bit limit)
|
||||
with pytest.raises(ValueError):
|
||||
_validate_discord_id("99999999999999999999")
|
||||
@@ -64,7 +65,7 @@ class TestIdListParsing:
|
||||
("", []),
|
||||
(None, []),
|
||||
]
|
||||
|
||||
|
||||
for input_value, expected in test_cases:
|
||||
result = _parse_id_list(input_value)
|
||||
assert result == expected
|
||||
@@ -89,7 +90,7 @@ class TestIdListParsing:
|
||||
"123456789012345678\n234567890123456789", # newline
|
||||
"123456789012345678\r234567890123456789", # carriage return
|
||||
]
|
||||
|
||||
|
||||
for malicious_input in malicious_inputs:
|
||||
result = _parse_id_list(malicious_input)
|
||||
# Should filter out malicious entries
|
||||
@@ -106,7 +107,7 @@ class TestSettingsValidation:
|
||||
"Bot.MTIzNDU2Nzg5MDEyMzQ1Njc4.some_long_token_string_here",
|
||||
"a" * 60, # minimum reasonable length
|
||||
]
|
||||
|
||||
|
||||
for token in valid_tokens:
|
||||
settings = Settings(discord_token=token)
|
||||
assert settings.discord_token.get_secret_value() == token
|
||||
@@ -119,7 +120,7 @@ class TestSettingsValidation:
|
||||
"token with spaces", # contains spaces
|
||||
"token\nwith\nnewlines", # contains newlines
|
||||
]
|
||||
|
||||
|
||||
for token in invalid_tokens:
|
||||
with pytest.raises(ValidationError):
|
||||
Settings(discord_token=token)
|
||||
@@ -131,7 +132,7 @@ class TestSettingsValidation:
|
||||
settings = Settings(
|
||||
discord_token="valid_token_" + "a" * 50,
|
||||
ai_provider="anthropic",
|
||||
anthropic_api_key=valid_key
|
||||
anthropic_api_key=valid_key,
|
||||
)
|
||||
assert settings.anthropic_api_key.get_secret_value() == valid_key
|
||||
|
||||
@@ -140,23 +141,23 @@ class TestSettingsValidation:
|
||||
Settings(
|
||||
discord_token="valid_token_" + "a" * 50,
|
||||
ai_provider="anthropic",
|
||||
anthropic_api_key="short"
|
||||
anthropic_api_key="short",
|
||||
)
|
||||
|
||||
def test_configuration_validation_ai_provider(self):
|
||||
"""Test AI provider configuration validation."""
|
||||
settings = Settings(discord_token="valid_token_" + "a" * 50)
|
||||
|
||||
|
||||
# Should pass with no AI provider
|
||||
settings.ai_provider = "none"
|
||||
settings.validate_configuration()
|
||||
|
||||
|
||||
# Should fail with anthropic but no key
|
||||
settings.ai_provider = "anthropic"
|
||||
settings.anthropic_api_key = None
|
||||
with pytest.raises(ValueError, match="GUARDDEN_ANTHROPIC_API_KEY is required"):
|
||||
settings.validate_configuration()
|
||||
|
||||
|
||||
# Should pass with anthropic and key
|
||||
settings.anthropic_api_key = "sk-" + "a" * 50
|
||||
settings.validate_configuration()
|
||||
@@ -164,13 +165,13 @@ class TestSettingsValidation:
|
||||
def test_configuration_validation_database_pool(self):
|
||||
"""Test database pool configuration validation."""
|
||||
settings = Settings(discord_token="valid_token_" + "a" * 50)
|
||||
|
||||
|
||||
# Should fail with min > max
|
||||
settings.database_pool_min = 10
|
||||
settings.database_pool_max = 5
|
||||
with pytest.raises(ValueError, match="database_pool_min cannot be greater"):
|
||||
settings.validate_configuration()
|
||||
|
||||
|
||||
# Should fail with min < 1
|
||||
settings.database_pool_min = 0
|
||||
settings.database_pool_max = 5
|
||||
@@ -190,7 +191,7 @@ class TestSecurityImprovements:
|
||||
"123456789012345678\x00\x01\x02",
|
||||
"123456789012345678<script>alert('xss')</script>",
|
||||
]
|
||||
|
||||
|
||||
for attempt in injection_attempts:
|
||||
# Should either raise an error or filter out the malicious input
|
||||
try:
|
||||
@@ -205,33 +206,41 @@ class TestSecurityImprovements:
|
||||
def test_settings_with_malicious_env_vars(self):
|
||||
"""Test that settings handle malicious environment variables."""
|
||||
import os
|
||||
|
||||
|
||||
# Save original values
|
||||
original_guilds = os.environ.get("GUARDDEN_ALLOWED_GUILDS")
|
||||
original_owners = os.environ.get("GUARDDEN_OWNER_IDS")
|
||||
|
||||
|
||||
try:
|
||||
# Set malicious environment variables
|
||||
os.environ["GUARDDEN_ALLOWED_GUILDS"] = "123456789012345678\x00,malicious"
|
||||
os.environ["GUARDDEN_OWNER_IDS"] = "234567890123456789\n567890123456789012"
|
||||
|
||||
try:
|
||||
os.environ["GUARDDEN_ALLOWED_GUILDS"] = "123456789012345678\x00,malicious"
|
||||
except ValueError:
|
||||
os.environ["GUARDDEN_ALLOWED_GUILDS"] = "123456789012345678,malicious"
|
||||
try:
|
||||
os.environ["GUARDDEN_OWNER_IDS"] = "234567890123456789\n567890123456789012"
|
||||
except ValueError:
|
||||
os.environ["GUARDDEN_OWNER_IDS"] = "234567890123456789,567890123456789012"
|
||||
|
||||
settings = Settings(discord_token="valid_token_" + "a" * 50)
|
||||
|
||||
|
||||
# Should filter out malicious entries
|
||||
assert len(settings.allowed_guilds) <= 1
|
||||
assert len(settings.owner_ids) <= 1
|
||||
|
||||
|
||||
# Valid IDs should be preserved
|
||||
assert 123456789012345678 in settings.allowed_guilds or len(settings.allowed_guilds) == 0
|
||||
|
||||
assert (
|
||||
123456789012345678 in settings.allowed_guilds or len(settings.allowed_guilds) == 0
|
||||
)
|
||||
|
||||
finally:
|
||||
# Restore original values
|
||||
if original_guilds is not None:
|
||||
os.environ["GUARDDEN_ALLOWED_GUILDS"] = original_guilds
|
||||
else:
|
||||
os.environ.pop("GUARDDEN_ALLOWED_GUILDS", None)
|
||||
|
||||
|
||||
if original_owners is not None:
|
||||
os.environ["GUARDDEN_OWNER_IDS"] = original_owners
|
||||
else:
|
||||
os.environ.pop("GUARDDEN_OWNER_IDS", None)
|
||||
os.environ.pop("GUARDDEN_OWNER_IDS", None)
|
||||
|
||||
Reference in New Issue
Block a user