Skip to content

Commit

Permalink
fix(feature-flags): Add a setting to prevent certain teams from abusi…
Browse files Browse the repository at this point in the history
…ng decide endpoint (#24902)

Adds a DECIDE_SHORT_CIRCUITED_TEAM_IDS property settings/web.py which is then used by the /decide endpoint to disable access to the endpoint.
  • Loading branch information
Phanatic authored Sep 11, 2024
1 parent f01b44d commit 1a769ff
Show file tree
Hide file tree
Showing 3 changed files with 50 additions and 0 deletions.
11 changes: 11 additions & 0 deletions posthog/api/decide.py
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,17 @@ def get_decide(request: HttpRequest):
team = user.teams.get(id=project_id)

if team:
if team.id in settings.DECIDE_SHORT_CIRCUITED_TEAM_IDS:
return cors_response(
request,
generate_exception_response(
"decide",
f"Team with ID {team.id} cannot access the /decide endpoint."
f"Please contact us at [email protected]",
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
),
)

token = cast(str, token) # we know it's not None if we found a team
structlog.contextvars.bind_contextvars(team_id=team.id)

Expand Down
34 changes: 34 additions & 0 deletions posthog/api/test/test_decide.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
Plugin,
PluginConfig,
PluginSourceFile,
Project,
)
from posthog.models.cohort.cohort import Cohort
from posthog.models.feature_flag.feature_flag import FeatureFlagHashKeyOverride
Expand Down Expand Up @@ -2647,6 +2648,39 @@ def test_missing_token(self, *args):
response = self._post_decide({"distinct_id": "example_id", "api_key": None, "project_id": self.team.id})
self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)

def test_short_circuited_team(self, *args):
short_circuited_team_token = "short_circuited_team_token"

_, short_circuited_team = Project.objects.create_with_team(
organization=self.organization,
team_fields={
"api_token": short_circuited_team_token,
"test_account_filters": [
{
"key": "email",
"value": "@posthog.com",
"operator": "not_icontains",
"type": "person",
}
],
"has_completed_onboarding_for": {"product_analytics": True},
},
)
with self.settings(DECIDE_SHORT_CIRCUITED_TEAM_IDS=[short_circuited_team.id]):
response = self._post_decide(
{
"distinct_id": "example_id",
"api_key": short_circuited_team_token,
"project_id": short_circuited_team.id,
}
)
self.assertEqual(response.status_code, status.HTTP_422_UNPROCESSABLE_ENTITY)
response_data = response.json()
self.assertEqual(
response_data["detail"],
f"Team with ID {short_circuited_team.id} cannot access the /decide endpoint.Please contact us at [email protected]",
)

def test_invalid_payload_on_decide_endpoint(self, *args):
invalid_payloads = [
base64.b64encode(b"1-1").decode("utf-8"),
Expand Down
5 changes: 5 additions & 0 deletions posthog/settings/web.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,11 @@
DECIDE_BUCKET_CAPACITY = get_from_env("DECIDE_BUCKET_CAPACITY", type_cast=int, default=500)
DECIDE_BUCKET_REPLENISH_RATE = get_from_env("DECIDE_BUCKET_REPLENISH_RATE", type_cast=float, default=10.0)

# Prevent decide abuse

# This is a list of team-ids that are prevented from using the /decide endpoint
# until they fix an issue with their feature flags causing instability in posthog.
DECIDE_SHORT_CIRCUITED_TEAM_IDS = [15611]
# Decide db settings

DECIDE_SKIP_POSTGRES_FLAGS = get_from_env("DECIDE_SKIP_POSTGRES_FLAGS", False, type_cast=str_to_bool)
Expand Down

0 comments on commit 1a769ff

Please sign in to comment.