Files
prowler/tests/providers/vercel/vercel_provider_test.py

274 lines
9.1 KiB
Python

import os
from unittest import mock
from unittest.mock import MagicMock, patch
import pytest
from prowler.providers.common.models import Connection
from prowler.providers.vercel.exceptions.exceptions import (
VercelAuthenticationError,
VercelCredentialsError,
)
from prowler.providers.vercel.models import VercelIdentityInfo, VercelSession
from prowler.providers.vercel.vercel_provider import VercelProvider
from tests.providers.vercel.vercel_fixtures import (
API_TOKEN,
TEAM_ID,
TEAM_NAME,
TEAM_SLUG,
USER_EMAIL,
USER_ID,
USERNAME,
)
class TestVercelProviderSetupSession:
def test_setup_session_with_env_var(self):
with mock.patch.dict(os.environ, {"VERCEL_TOKEN": API_TOKEN}, clear=False):
session = VercelProvider.setup_session()
assert isinstance(session, VercelSession)
assert session.token == API_TOKEN
assert session.http_session is not None
def test_setup_session_with_api_token_param(self):
with mock.patch.dict(os.environ, {}, clear=True):
session = VercelProvider.setup_session(api_token=API_TOKEN)
assert isinstance(session, VercelSession)
assert session.token == API_TOKEN
assert session.http_session is not None
def test_setup_session_with_team_id_param(self):
with mock.patch.dict(os.environ, {}, clear=True):
session = VercelProvider.setup_session(api_token=API_TOKEN, team_id=TEAM_ID)
assert session.token == API_TOKEN
assert session.team_id == TEAM_ID
def test_setup_session_no_credentials_raises(self):
with mock.patch.dict(os.environ, {}, clear=True):
os.environ.pop("VERCEL_TOKEN", None)
with pytest.raises(VercelCredentialsError):
VercelProvider.setup_session()
def test_setup_session_team_from_env(self):
with mock.patch.dict(
os.environ, {"VERCEL_TOKEN": API_TOKEN, "VERCEL_TEAM": TEAM_ID}
):
session = VercelProvider.setup_session()
assert session.team_id == TEAM_ID
class TestVercelProviderSetupIdentity:
def test_setup_identity_with_team(self):
mock_session = VercelSession(
token=API_TOKEN, team_id=TEAM_ID, http_session=MagicMock()
)
# Mock user response
user_response = MagicMock()
user_response.status_code = 200
user_response.json.return_value = {
"user": {
"id": USER_ID,
"username": USERNAME,
"email": USER_EMAIL,
}
}
user_response.raise_for_status = MagicMock()
# Mock team response
team_response = MagicMock()
team_response.status_code = 200
team_response.json.return_value = {
"id": TEAM_ID,
"name": TEAM_NAME,
"slug": TEAM_SLUG,
}
team_response.raise_for_status = MagicMock()
def mock_get(url, **kwargs):
if "/v2/user" in url:
return user_response
if f"/v2/teams/{TEAM_ID}" in url:
return team_response
return MagicMock()
mock_session.http_session.get = mock_get
identity = VercelProvider.setup_identity(mock_session)
assert isinstance(identity, VercelIdentityInfo)
assert identity.user_id == USER_ID
assert identity.username == USERNAME
assert identity.email == USER_EMAIL
assert identity.team is not None
assert identity.team.id == TEAM_ID
assert identity.team.name == TEAM_NAME
assert identity.team.slug == TEAM_SLUG
def test_setup_identity_personal_account(self):
mock_session = VercelSession(
token=API_TOKEN, team_id=None, http_session=MagicMock()
)
user_response = MagicMock()
user_response.status_code = 200
user_response.json.return_value = {
"user": {
"id": USER_ID,
"username": USERNAME,
"email": USER_EMAIL,
}
}
user_response.raise_for_status = MagicMock()
mock_session.http_session.get = MagicMock(return_value=user_response)
identity = VercelProvider.setup_identity(mock_session)
assert identity.team is None
assert identity.user_id == USER_ID
class TestVercelProviderValidateCredentials:
def test_valid_credentials(self):
mock_session = VercelSession(
token=API_TOKEN, team_id=None, http_session=MagicMock()
)
response = MagicMock()
response.status_code = 200
response.raise_for_status = MagicMock()
mock_session.http_session.get = MagicMock(return_value=response)
# Should not raise
VercelProvider.validate_credentials(mock_session)
def test_invalid_token_raises(self):
mock_session = VercelSession(
token="invalid", team_id=None, http_session=MagicMock()
)
response = MagicMock()
response.status_code = 401
mock_session.http_session.get = MagicMock(return_value=response)
with pytest.raises(VercelAuthenticationError):
VercelProvider.validate_credentials(mock_session)
def test_forbidden_raises(self):
mock_session = VercelSession(
token=API_TOKEN, team_id=None, http_session=MagicMock()
)
response = MagicMock()
response.status_code = 403
mock_session.http_session.get = MagicMock(return_value=response)
with pytest.raises(VercelAuthenticationError):
VercelProvider.validate_credentials(mock_session)
class TestVercelProviderTestConnection:
@patch.object(VercelProvider, "validate_credentials")
@patch.object(VercelProvider, "setup_session")
def test_successful_connection(self, mock_setup_session, mock_validate):
mock_setup_session.return_value = VercelSession(
token=API_TOKEN, team_id=None, http_session=MagicMock()
)
mock_validate.return_value = None
result = VercelProvider.test_connection(raise_on_exception=False)
assert isinstance(result, Connection)
assert result.is_connected is True
@patch.object(VercelProvider, "validate_credentials")
@patch.object(VercelProvider, "setup_session")
def test_successful_connection_with_params(self, mock_setup_session, mock_validate):
mock_setup_session.return_value = VercelSession(
token=API_TOKEN, team_id=TEAM_ID, http_session=MagicMock()
)
mock_validate.return_value = None
result = VercelProvider.test_connection(
api_token=API_TOKEN, team_id=TEAM_ID, raise_on_exception=False
)
assert isinstance(result, Connection)
assert result.is_connected is True
mock_setup_session.assert_called_once_with(api_token=API_TOKEN, team_id=TEAM_ID)
@patch.object(VercelProvider, "setup_session")
def test_failed_connection_no_credentials(self, mock_setup_session):
mock_setup_session.side_effect = VercelCredentialsError(
message="No credentials"
)
result = VercelProvider.test_connection(raise_on_exception=False)
assert isinstance(result, Connection)
assert result.is_connected is False
assert result.error is not None
@patch.object(VercelProvider, "setup_session")
def test_failed_connection_raises(self, mock_setup_session):
mock_setup_session.side_effect = VercelCredentialsError(
message="No credentials"
)
with pytest.raises(VercelCredentialsError):
VercelProvider.test_connection(raise_on_exception=True)
class TestVercelSessionTokenSecurity:
"""The Vercel API token must never leak through serialization or repr."""
def test_token_is_still_accessible_as_attribute(self):
session = VercelSession(token=API_TOKEN, team_id=TEAM_ID)
assert session.token == API_TOKEN
def test_token_excluded_from_dict(self):
session = VercelSession(token=API_TOKEN, team_id=TEAM_ID)
serialized = session.dict()
assert "token" not in serialized
assert API_TOKEN not in str(serialized)
assert serialized["team_id"] == TEAM_ID
def test_token_excluded_from_model_dump(self):
session = VercelSession(token=API_TOKEN, team_id=TEAM_ID)
serialized = session.model_dump()
assert "token" not in serialized
assert API_TOKEN not in str(serialized)
def test_token_excluded_from_json(self):
session = VercelSession(token=API_TOKEN, team_id=TEAM_ID)
serialized = session.json()
assert "token" not in serialized
assert API_TOKEN not in serialized
def test_token_excluded_from_model_dump_json(self):
session = VercelSession(token=API_TOKEN, team_id=TEAM_ID)
serialized = session.model_dump_json()
assert "token" not in serialized
assert API_TOKEN not in serialized
def test_token_excluded_from_repr_and_str(self):
session = VercelSession(token=API_TOKEN, team_id=TEAM_ID)
assert API_TOKEN not in repr(session)
assert API_TOKEN not in str(session)
assert "token" not in repr(session)