mirror of
https://github.com/prowler-cloud/prowler.git
synced 2026-07-24 13:01:56 +00:00
466f1a3d73
Co-authored-by: Hugo P.Brito <hugopbrit@gmail.com> Co-authored-by: Hugo Pereira Brito <101209179+HugoPBrito@users.noreply.github.com>
186 lines
5.8 KiB
Python
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 == {}
|