feat(seer-infra-telemetry): Add GCP connection verification endpoint - #122107
feat(seer-infra-telemetry): Add GCP connection verification endpoint#122107shashjar wants to merge 2 commits into
Conversation
|
bugbot run |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit d412869. Configure here.
| except IntegrationError as exc: | ||
| return Response({"detail": str(exc)}, status=502) | ||
|
|
||
| return Response(result) |
There was a problem hiding this comment.
API response uses snake_case keys
Medium Severity
The success path returns Seer's payload as-is with snake_case keys like connection_status and error_detail. Sentry organization APIs use camelCase response bodies, and this endpoint's own validation errors already come back camelCase via CamelSnakeSerializer, so clients get inconsistent key casing between 400 and 200 responses.
Reviewed by Cursor Bugbot for commit d412869. Configure here.
Backend Test FailuresFailures on
|
| from sentry.seer.signed_seer_api import ( | ||
| make_signed_seer_api_request, | ||
| seer_autofix_default_connection_pool, | ||
| ) | ||
| from sentry.shared_integrations.exceptions import IntegrationError | ||
|
|
||
| logger = logging.getLogger(__name__) | ||
|
|
||
| _VERIFY_CONNECTION_TIMEOUT = 60 | ||
|
|
There was a problem hiding this comment.
Organization endpoint allows arbitrary GCP Sentry service-account impersonation
A member with access to one organization can submit another organization’s sentry_sa_email and have Sentry’s signed Seer proxy attempt the GCP impersonation chain without checking the email against the URL organization. Bind the Sentry service account to organization.id server-side before forwarding the request.
Evidence
GcpVerifyConnectionSerializeracceptssentry_sa_emailfrom the request, andOrganizationMonitoringProviderVerifyConnectionEndpoint.postforwards it directly toverify_gcp_connectionafter only the feature and organization-scope permission checks.verify_gcp_connectionserializes that value into the signed request to Seer’s/v1/monitoring-providers/gcp/verify-connection; the request body contains no organization binding or ownership assertion.GcpServiceAccountstores the generated service-account email with a uniqueorganization_id, but this endpoint never reads or compares that mapping.- Seer’s
GcpSaImpersonationConnectionDatadocuments thatsentry_sa_emailselects the Sentry service account in the ADC → per-customer service account → customer service account chain. Consequently, a caller who obtains another organization’s connector credentials can invoke the verification path through their own organization URL and receive the resulting connection status.
Also found at 1 additional location
src/sentry/api/endpoints/organization_monitoring_provider_verify_connection.py:45-50
Identified by Warden · security-review · JAV-EG4
| from typing import Any, TypedDict | ||
|
|
||
| import google.auth | ||
| import orjson |
There was a problem hiding this comment.
orjson.loads on Seer response without JSONDecodeError handling
orjson.loads(response.data) in verify_gcp_connection parses the Seer response without catching orjson.JSONDecodeError. A malformed or empty response body would raise an unhandled exception, causing a 500 instead of a graceful error.
Evidence
- verify_gcp_connection calls make_signed_seer_api_request to POST /v1/monitoring-providers/gcp/verify-connection.
- On HTTP 200, it unconditionally calls orjson.loads(response.data) before returning.
- orjson.loads raises orjson.JSONDecodeError on invalid JSON, which is not caught by the endpoint's except IntegrationError.
- Seer could return an HTML error page or truncated body even with a 200 status, matching the JSONDecodeError production bug class.
Also found at 2 additional locations
src/sentry/api/endpoints/organization_monitoring_provider_verify_connection.py:46-50src/sentry/integrations/gcp/client.py:162-162
Identified by Warden · sentry-backend-bugs · 72N-7ED
| from requests.exceptions import RequestException | ||
|
|
||
| from sentry.integrations.models.gcp_service_account import GcpServiceAccount | ||
| from sentry.seer.signed_seer_api import ( |
There was a problem hiding this comment.
Unhandled transport errors from internal Seer API call
make_signed_seer_api_request can raise urllib3 connection and timeout errors that are not caught by the endpoint's except IntegrationError, causing 500 responses when Seer is unreachable or the request times out.
Evidence
make_signed_seer_api_requestdirectly callsconnection_pool.urlopen()which propagates raw urllib3 exceptions (TimeoutError,MaxRetryError,HTTPError, etc.) without wrapping them.verify_gcp_connectioninclient.pyinvokesmake_signed_seer_api_requestwith no try/except around the call (line ~147).- The endpoint
OrganizationMonitoringProviderVerifyConnectionEndpoint.postonly catchesIntegrationError, so any urllib3 transport error bubbles up as an unhandled 500. - The same file (
client.py) already follows the safe pattern ingenerate_sentry_sa, which catchesRequestExceptionand re-raises asIntegrationError. - Multiple other call sites in the codebase (e.g.,
similar_issues.py,anomaly_detection/get_anomaly_data.py) explicitly catch(TimeoutError, MaxRetryError)aroundmake_signed_seer_api_requestcalls.
Identified by Warden · sentry-backend-bugs · MQK-3PN
| logger.error( | ||
| "gcp.verify_connection_failed", | ||
| extra={"status_code": response.status}, | ||
| ) |
There was a problem hiding this comment.
verify_gcp_connection forwards full Seer response without field filtering
Returns the parsed Seer JSON directly, so any extra fields added by the upstream endpoint will leak to the caller.
Evidence
verify_gcp_connectionsends a signed request to Seer at/v1/monitoring-providers/gcp/verify-connectionand returnsorjson.loads(response.data)verbatim.- The
GcpVerifyConnectionResultTypedDict does not enforce its shape at runtime, so extra keys from the Seer payload are preserved. OrganizationMonitoringProviderVerifyConnectionEndpoint.postpasses the returned dict directly intoResponse(result), forwarding every key to the client.- Sentry commit
0c0aae90ac1fixed the same class of bug where an internal-service response was returned to the client without a serializer, leaking additional fields.
Identified by Warden · wrdn-data-exfil · 46C-JS8


PR 2 for https://linear.app/getsentry/issue/CW-1662/add-gcp-connection-verification-endpoint
Adds a
POST /api/0/organizations/{org}/monitoring-providers/gcp/verify-connection/endpoint - a thin proxy that forwards verification requests to Seer's/v1/monitoring-providers/gcp/verify-connectionendpoint (added in https://github.com/getsentry/seer/pull/7792).This endpoint will be used when orgs are setting up the GCP integration in order to verify connection status.