Skip to content

fix(api): Add org membership check to onboarding continuation email endpoint#108474

Merged
dcramer merged 2 commits intomasterfrom
fix/onboarding-email-org-membership-check
Feb 18, 2026
Merged

fix(api): Add org membership check to onboarding continuation email endpoint#108474
dcramer merged 2 commits intomasterfrom
fix/onboarding-email-org-membership-check

Conversation

@dcramer
Copy link
Copy Markdown
Member

@dcramer dcramer commented Feb 18, 2026

The OrganizationOnboardingContinuationEmail endpoint used SentryIsAuthenticated as its
permission class, which only verifies that the user is logged in. This allowed any authenticated
user to trigger onboarding continuation emails for any organization, leaking the org name in
the email and polluting analytics with bogus OnboardingContinuationSent events.

The code comment states the intent was to "let anyone in the org use this endpoint," but
SentryIsAuthenticated doesn't perform any org membership check — it delegates to
BasePermission.has_object_permission which unconditionally returns True.

This replaces SentryIsAuthenticated with an OrganizationPermission-based permission class
that requires org:read scope for POST, matching the pattern used by the sibling
OrganizationOnboardingTaskEndpoint. This ensures org membership is verified via
determine_access() while still allowing any org member (including the lowest member role)
to use the endpoint.

…ndpoint

The endpoint used SentryIsAuthenticated which only checks if the user is
logged in, allowing any authenticated user to trigger onboarding emails
for any organization. Replace with OrganizationPermission-based check
that verifies org membership while still allowing any org member (even
the member role with only org:read) to use the endpoint.

Co-Authored-By: Claude <[email protected]>
@github-actions github-actions bot added the Scope: Backend Automatically applied to PRs that change backend components label Feb 18, 2026
@dcramer dcramer marked this pull request as ready for review February 18, 2026 19:00
@dcramer dcramer requested review from a team as code owners February 18, 2026 19:00
"POST": ApiPublishStatus.PRIVATE,
}
owner = ApiOwner.TELEMETRY_EXPERIENCE
# let anyone in the org use this endpoint
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

so is the comment off here? SentryIsAuthenticated means that anyone outside the org can access the endpoint? i assume there are a bunch of other org scoped endpoints that use this permission which we'll have to change as well?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, the comment was wrong — SentryIsAuthenticated only checks that the user is logged in. On an OrganizationEndpoint, the org membership check happens in OrganizationPermission.has_object_permission() (via determine_access()), so overriding permission_classes with just SentryIsAuthenticated bypasses it entirely. Any authenticated user could hit this endpoint for any org.

I audited all endpoints using SentryIsAuthenticated and this was the only org-scoped endpoint using it as the sole permission class. The one other org endpoint that includes it (DiscoverHomepageQueryEndpoint) also has DiscoverSavedQueryPermission alongside it, so it's still protected by DRF's AND logic — though the SentryIsAuthenticated there is redundant and could be cleaned up separately.

I also removed the misleading comment in 2950c40.

The comment said "let anyone in the org use this endpoint" but the
previous permission class (SentryIsAuthenticated) didn't actually
enforce org membership. Now that the permission class is correct,
the comment is redundant and could mislead future readers.
@dcramer dcramer enabled auto-merge (squash) February 18, 2026 19:22
@dcramer dcramer merged commit dffc7e9 into master Feb 18, 2026
77 checks passed
@dcramer dcramer deleted the fix/onboarding-email-org-membership-check branch February 18, 2026 19:43
JonasBa pushed a commit that referenced this pull request Feb 19, 2026
…ndpoint (#108474)

The `OrganizationOnboardingContinuationEmail` endpoint used
`SentryIsAuthenticated` as its
permission class, which only verifies that the user is logged in. This
allowed any authenticated
user to trigger onboarding continuation emails for any organization,
leaking the org name in
the email and polluting analytics with bogus
`OnboardingContinuationSent` events.

The code comment states the intent was to "let anyone in the org use
this endpoint," but
`SentryIsAuthenticated` doesn't perform any org membership check — it
delegates to
`BasePermission.has_object_permission` which unconditionally returns
`True`.

This replaces `SentryIsAuthenticated` with an
`OrganizationPermission`-based permission class
that requires `org:read` scope for POST, matching the pattern used by
the sibling
`OrganizationOnboardingTaskEndpoint`. This ensures org membership is
verified via
`determine_access()` while still allowing any org member (including the
lowest `member` role)
to use the endpoint.

---------

Co-authored-by: Claude <[email protected]>
mchen-sentry pushed a commit that referenced this pull request Feb 24, 2026
…ndpoint (#108474)

The `OrganizationOnboardingContinuationEmail` endpoint used
`SentryIsAuthenticated` as its
permission class, which only verifies that the user is logged in. This
allowed any authenticated
user to trigger onboarding continuation emails for any organization,
leaking the org name in
the email and polluting analytics with bogus
`OnboardingContinuationSent` events.

The code comment states the intent was to "let anyone in the org use
this endpoint," but
`SentryIsAuthenticated` doesn't perform any org membership check — it
delegates to
`BasePermission.has_object_permission` which unconditionally returns
`True`.

This replaces `SentryIsAuthenticated` with an
`OrganizationPermission`-based permission class
that requires `org:read` scope for POST, matching the pattern used by
the sibling
`OrganizationOnboardingTaskEndpoint`. This ensures org membership is
verified via
`determine_access()` while still allowing any org member (including the
lowest `member` role)
to use the endpoint.

---------

Co-authored-by: Claude <[email protected]>
@github-actions github-actions bot locked and limited conversation to collaborators Mar 6, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

Scope: Backend Automatically applied to PRs that change backend components

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants