Files
prowler/tests/config/schema/other_providers_schema_test.py

265 lines
9.8 KiB
Python

"""Smaller-provider schema coverage. One happy path + one invalid path
per field is enough to lock in the contract; the validator behaviour
itself is covered exhaustively in validator_test.py."""
import pytest
from prowler.config.schema.registry import SCHEMAS
from prowler.config.schema.validator import validate_provider_config
def _validate(provider, raw):
return validate_provider_config(provider, raw, SCHEMAS[provider])
class Test_Azure_Schema:
@pytest.mark.parametrize("level", ["Low", "Medium", "High", "Critical"])
def test_defender_risk_level_valid_values(self, level):
assert _validate(
"azure", {"defender_attack_path_minimal_risk_level": level}
) == {"defender_attack_path_minimal_risk_level": level}
def test_defender_risk_level_lowercase_dropped(self):
# Case matters: the matching check uses Title-case comparison.
assert (
_validate("azure", {"defender_attack_path_minimal_risk_level": "high"})
== {}
)
def test_apim_threshold_in_range(self):
out = _validate("azure", {"apim_threat_detection_llm_jacking_threshold": 0.1})
assert out == {"apim_threat_detection_llm_jacking_threshold": 0.1}
def test_apim_threshold_out_of_range(self):
out = _validate("azure", {"apim_threat_detection_llm_jacking_threshold": 1.5})
assert out == {}
def test_vm_backup_retention_must_be_positive(self):
assert _validate("azure", {"vm_backup_min_daily_retention_days": 7}) == {
"vm_backup_min_daily_retention_days": 7
}
assert _validate("azure", {"vm_backup_min_daily_retention_days": 0}) == {}
assert _validate("azure", {"vm_backup_min_daily_retention_days": -1}) == {}
class Test_GCP_Schema:
def test_valid_values_round_trip(self):
raw = {
"mig_min_zones": 2,
"max_snapshot_age_days": 90,
"max_unused_account_days": 180,
"storage_min_retention_days": 90,
}
assert _validate("gcp", raw) == raw
def test_zero_zone_count_dropped(self):
assert _validate("gcp", {"mig_min_zones": 0}) == {}
class Test_Kubernetes_Schema:
def test_valid_values_round_trip(self):
raw = {
"audit_log_maxbackup": 10,
"audit_log_maxsize": 100,
"audit_log_maxage": 30,
}
assert _validate("kubernetes", raw) == raw
def test_negative_audit_log_dropped(self):
assert _validate("kubernetes", {"audit_log_maxage": -1}) == {}
class Test_M365_Schema:
def test_valid_values_round_trip(self):
raw = {
"sign_in_frequency": 4,
"recommended_mailtips_large_audience_threshold": 25,
"audit_log_age": 90,
}
assert _validate("m365", raw) == raw
def test_negative_audit_log_age_dropped(self):
assert _validate("m365", {"audit_log_age": -10}) == {}
class Test_GitHub_Schema:
def test_valid_threshold(self):
assert _validate("github", {"inactive_not_archived_days_threshold": 180}) == {
"inactive_not_archived_days_threshold": 180
}
def test_zero_threshold_dropped(self):
assert _validate("github", {"inactive_not_archived_days_threshold": 0}) == {}
class Test_MongoDBAtlas_Schema:
def test_valid(self):
assert _validate(
"mongodbatlas", {"max_service_account_secret_validity_hours": 8}
) == {"max_service_account_secret_validity_hours": 8}
def test_invalid_negative(self):
assert (
_validate("mongodbatlas", {"max_service_account_secret_validity_hours": -1})
== {}
)
class Test_Cloudflare_Schema:
def test_zero_retries_allowed(self):
# 0 is explicitly documented as "disable retries" in config.yaml.
assert _validate("cloudflare", {"max_retries": 0}) == {"max_retries": 0}
def test_positive_retries_allowed(self):
assert _validate("cloudflare", {"max_retries": 3}) == {"max_retries": 3}
def test_negative_retries_dropped(self):
assert _validate("cloudflare", {"max_retries": -1}) == {}
class Test_Okta_Schema:
def test_valid_values_round_trip(self):
raw = {
"okta_max_session_idle_minutes": 15,
"okta_max_session_lifetime_minutes": 18 * 60,
"okta_admin_console_idle_timeout_max_minutes": 15,
"okta_user_inactivity_max_days": 35,
"okta_dod_approved_ca_issuer_patterns": [r"\bOU=DoD\b", r"\bOU=ECA\b"],
}
assert _validate("okta", raw) == raw
def test_zero_idle_minutes_dropped(self):
assert _validate("okta", {"okta_max_session_idle_minutes": 0}) == {}
def test_negative_inactivity_days_dropped(self):
assert _validate("okta", {"okta_user_inactivity_max_days": -1}) == {}
def test_full_rate_limit_config_round_trip(self):
raw = {
"okta_requests_per_second": 4.0,
"okta_max_retries": 5,
"okta_request_timeout": 300,
}
assert _validate("okta", raw) == raw
def test_requests_per_second_zero_allowed(self):
# 0 is documented as "disable throttling" in config.yaml.
assert _validate("okta", {"okta_requests_per_second": 0}) == {
"okta_requests_per_second": 0
}
def test_requests_per_second_sub_one_allowed(self):
assert _validate("okta", {"okta_requests_per_second": 0.5}) == {
"okta_requests_per_second": 0.5
}
def test_requests_per_second_floor_allowed(self):
# 0.1 is the lowest non-zero rate accepted.
assert _validate("okta", {"okta_requests_per_second": 0.1}) == {
"okta_requests_per_second": 0.1
}
def test_requests_per_second_below_floor_dropped(self):
# A tiny rate (e.g. 0.001 -> ~1000s/request) would make scans take
# days or years; it must be rejected, not silently honoured.
assert _validate("okta", {"okta_requests_per_second": 0.001}) == {}
assert _validate("okta", {"okta_requests_per_second": 0.05}) == {}
def test_requests_per_second_above_max_dropped(self):
assert _validate("okta", {"okta_requests_per_second": 101}) == {}
def test_requests_per_second_negative_dropped(self):
assert _validate("okta", {"okta_requests_per_second": -1}) == {}
def test_max_retries_zero_allowed(self):
assert _validate("okta", {"okta_max_retries": 0}) == {"okta_max_retries": 0}
def test_max_retries_out_of_range_dropped(self):
assert _validate("okta", {"okta_max_retries": -1}) == {}
assert _validate("okta", {"okta_max_retries": 11}) == {}
def test_request_timeout_zero_allowed(self):
# 0 is documented as "disable the timeout" in config.yaml.
assert _validate("okta", {"okta_request_timeout": 0}) == {
"okta_request_timeout": 0
}
def test_request_timeout_in_range_allowed(self):
assert _validate("okta", {"okta_request_timeout": 300}) == {
"okta_request_timeout": 300
}
def test_request_timeout_out_of_range_dropped(self):
assert _validate("okta", {"okta_request_timeout": -1}) == {}
assert _validate("okta", {"okta_request_timeout": 3601}) == {}
def test_non_numeric_value_dropped(self):
# A typo'd string must not flow through to the limiter (it would crash
# the `> 0` comparison during provider init).
assert _validate("okta", {"okta_requests_per_second": "fast"}) == {}
class Test_Vercel_Schema:
def test_owner_percentage_in_range(self):
assert _validate("vercel", {"max_owner_percentage": 20}) == {
"max_owner_percentage": 20
}
assert _validate("vercel", {"max_owner_percentage": 1}) == {
"max_owner_percentage": 1
}
assert _validate("vercel", {"max_owner_percentage": 50}) == {
"max_owner_percentage": 50
}
def test_owner_percentage_over_max_dropped(self):
# Tightened to 1..50 — anything above (incl. previous 100) is dropped.
assert _validate("vercel", {"max_owner_percentage": 51}) == {}
assert _validate("vercel", {"max_owner_percentage": 150}) == {}
def test_owner_percentage_zero_or_negative_dropped(self):
# 0 is no longer a valid configuration (defeats PoLP signal).
assert _validate("vercel", {"max_owner_percentage": 0}) == {}
assert _validate("vercel", {"max_owner_percentage": -1}) == {}
def test_full_default_config_round_trip(self):
raw = {
"stable_branches": ["main", "master"],
"days_to_expire_threshold": 7,
"stale_token_threshold_days": 90,
"stale_invitation_threshold_days": 30,
"max_owner_percentage": 20,
"max_owners": 3,
"secret_suffixes": ["_KEY", "_SECRET", "_TOKEN"],
}
assert _validate("vercel", raw) == raw
class Test_AlibabaCloud_Schema:
def test_valid_values_round_trip(self):
raw = {
"max_cluster_check_days": 7,
"max_console_access_days": 90,
"min_log_retention_days": 365,
"min_rds_audit_retention_days": 180,
}
assert _validate("alibabacloud", raw) == raw
def test_zero_cluster_check_days_dropped(self):
assert _validate("alibabacloud", {"max_cluster_check_days": 0}) == {}
def test_console_access_below_min_dropped(self):
# 30 is the documented floor; anything below produces false positives.
assert _validate("alibabacloud", {"max_console_access_days": 29}) == {}
class Test_OpenStack_Schema:
def test_valid_values_round_trip(self):
raw = {
"image_sharing_threshold": 5,
"secrets_ignore_patterns": ["AKIA[0-9A-Z]{16}"],
}
assert _validate("openstack", raw) == raw
def test_zero_threshold_dropped(self):
assert _validate("openstack", {"image_sharing_threshold": 0}) == {}