diff --git a/prowler/CHANGELOG.md b/prowler/CHANGELOG.md index 1d8d462738..7f8d5c7051 100644 --- a/prowler/CHANGELOG.md +++ b/prowler/CHANGELOG.md @@ -25,6 +25,7 @@ All notable changes to the **Prowler SDK** are documented in this file. - `zone_waf_enabled` check for Cloudflare provider now appends a plan-aware hint to the FAIL `status_extended`: a possible-false-positive note on paid plans (Pro, Business, Enterprise) where the legacy `waf` zone setting can read `off` even though WAF managed rulesets are deployed via the dashboard, and a "not available on the Cloudflare Free plan" note on Free zones [(#9896)](https://github.com/prowler-cloud/prowler/pull/9896) - Google Workspace Gmail checks sharing a single resource row, causing the service field to be overwritten by the last check executed [(#11169)](https://github.com/prowler-cloud/prowler/pull/11169) - Google Workspace Drive and Calendar services missing server-side policy filters [(#11195)](https://github.com/prowler-cloud/prowler/pull/11195) +- `VercelSession.token` is now excluded from serialization and representation to prevent the Vercel API token from leaking through `.dict()`, `.json()` or logs [(#11198)](https://github.com/prowler-cloud/prowler/pull/11198) --- diff --git a/prowler/providers/vercel/models.py b/prowler/providers/vercel/models.py index 5f730bde76..d83e043d1e 100644 --- a/prowler/providers/vercel/models.py +++ b/prowler/providers/vercel/models.py @@ -9,7 +9,7 @@ from prowler.providers.common.models import ProviderOutputOptions class VercelSession(BaseModel): """Vercel API session information.""" - token: str + token: str = Field(exclude=True, repr=False) team_id: Optional[str] = None base_url: str = "https://api.vercel.com" http_session: Any = Field(default=None, exclude=True) diff --git a/tests/providers/vercel/vercel_provider_test.py b/tests/providers/vercel/vercel_provider_test.py index 56117ae6c4..a09e0b6d14 100644 --- a/tests/providers/vercel/vercel_provider_test.py +++ b/tests/providers/vercel/vercel_provider_test.py @@ -222,3 +222,52 @@ class TestVercelProviderTestConnection: 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)