Files
2026-06-08 14:59:50 +02:00

186 lines
5.8 KiB
Python

import json
from unittest import mock
from prowler.providers.okta.models import OktaIdentityInfo
from prowler.providers.okta.services.systemlog.systemlog_service import (
LogStream,
SystemLog,
)
from tests.providers.okta.okta_fixtures import (
OKTA_CLIENT_ID,
OKTA_ORG_DOMAIN,
set_mocked_okta_provider,
)
def _resp(headers: dict = None):
r = mock.MagicMock()
r.headers = headers or {}
return r
def _fake_stream(
stream_id: str, name: str, status: str = "ACTIVE", type_: str = "AWS_EVENTBRIDGE"
):
s = mock.MagicMock()
s.id = stream_id
s.name = name
s.status = status
s.type = type_
return s
def _patch_sdk(**methods):
return mock.patch(
"prowler.providers.okta.lib.service.service.OktaSDKClient",
return_value=mock.MagicMock(**methods),
)
class Test_SystemLog_service:
def test_fetches_active_streams(self):
provider = set_mocked_okta_provider()
s1 = _fake_stream("log-1", "EventBridge")
s2 = _fake_stream("log-2", "Splunk", type_="SPLUNK_CLOUD_LOGSTREAMING")
async def fake_list(*_a, **_k):
return ([s1, s2], _resp({}), None)
with _patch_sdk(list_log_streams=fake_list):
service = SystemLog(provider)
assert set(service.log_streams.keys()) == {"log-1", "log-2"}
assert isinstance(service.log_streams["log-1"], LogStream)
assert service.log_streams["log-2"].type == "SPLUNK_CLOUD_LOGSTREAMING"
def test_returns_empty_on_api_error(self):
provider = set_mocked_okta_provider()
async def failing(*_a, **_k):
return ([], _resp({}), Exception("E0000007"))
with _patch_sdk(list_log_streams=failing):
service = SystemLog(provider)
assert service.log_streams == {}
def test_skips_fetch_when_scope_missing(self):
identity = OktaIdentityInfo(
org_domain=OKTA_ORG_DOMAIN,
client_id=OKTA_CLIENT_ID,
granted_scopes=["okta.policies.read"], # no logStreams scope
)
provider = set_mocked_okta_provider(identity=identity)
called = False
async def fake_list(*_a, **_k):
nonlocal called
called = True
return ([], _resp({}), None)
with _patch_sdk(list_log_streams=fake_list):
service = SystemLog(provider)
assert called is False
assert service.log_streams == {}
assert service.missing_scope["log_streams"] == "okta.logStreams.read"
class Test_SystemLog_service_sdk_validation_fallback:
"""Verifies the raw-JSON fallback when the Okta SDK rejects API values.
The SDK's `LogStreamSettingsAws.eventSourceName` validator uses the
regex `^[a-zA-Z0-9.\\-_]$` — missing the `+` quantifier, so every
multi-character name raises pydantic `ValidationError`. Without the
fallback the whole stream list is lost; with it, the raw JSON path
still surfaces each stream's id/name/status/type.
"""
def test_raw_fallback_projects_streams_when_sdk_raises(self):
from pydantic import ValidationError
provider = set_mocked_okta_provider()
raw_payload = [
{
"id": "log-1",
"name": "EventBridge prod",
"status": "ACTIVE",
"type": "AWS_EVENTBRIDGE",
},
{
"id": "log-2",
"name": "Splunk staging",
"status": "INACTIVE",
"type": "SPLUNK_CLOUD_LOGSTREAMING",
},
]
async def failing_list_log_streams(*_a, **_k):
try:
# Trigger a real pydantic ValidationError so we exercise
# the exact exception type the SDK raises in production.
from okta.models.log_stream_settings_aws import LogStreamSettingsAws
LogStreamSettingsAws(
accountId="123456789012",
eventSourceName="MultiCharacter",
region="us-east-1",
)
except ValidationError as ve:
raise ve
return ([], _resp({}), None)
async def fake_raw_create(*_a, **_k):
return ({"url": "/api/v1/logStreams"}, None)
async def fake_raw_execute(_request):
return (None, json.dumps(raw_payload), None)
sdk = mock.MagicMock()
sdk.list_log_streams = failing_list_log_streams
sdk._request_executor.create_request = fake_raw_create
sdk._request_executor.execute = fake_raw_execute
with mock.patch(
"prowler.providers.okta.lib.service.service.OktaSDKClient",
return_value=sdk,
):
service = SystemLog(provider)
assert set(service.log_streams.keys()) == {"log-1", "log-2"}
assert service.log_streams["log-1"].status == "ACTIVE"
assert service.log_streams["log-2"].status == "INACTIVE"
assert service.log_streams["log-2"].type == "SPLUNK_CLOUD_LOGSTREAMING"
def test_raw_fallback_handles_empty_list(self):
from pydantic import ValidationError
provider = set_mocked_okta_provider()
async def failing(*_a, **_k):
raise ValidationError.from_exception_data(
title="LogStreamSettingsAws",
line_errors=[],
)
async def fake_create(*_a, **_k):
return ({"url": "/api/v1/logStreams"}, None)
async def fake_execute(_req):
return (None, "[]", None)
sdk = mock.MagicMock()
sdk.list_log_streams = failing
sdk._request_executor.create_request = fake_create
sdk._request_executor.execute = fake_execute
with mock.patch(
"prowler.providers.okta.lib.service.service.OktaSDKClient",
return_value=sdk,
):
service = SystemLog(provider)
assert service.log_streams == {}