Skip to content

Conversation

@nikkikapadia
Copy link
Member

@nikkikapadia nikkikapadia commented Nov 7, 2025

We've added a new extrapolation mode field in metrics alerts for EAP. We want to make sure the server_weighted value is not put in by the user. They also shouldn't be able to set this extrapolation mode if there already is another mode when updating. I've added tests to confirm but let me know if there are better ways to set and validate the extrapolation mode in the new alerts code.

@github-actions github-actions bot added the Scope: Backend Automatically applied to PRs that change backend components label Nov 7, 2025
Comment on lines 108 to 123
if (
new_extrapolation_mode == ExtrapolationMode.SERVER_WEIGHTED.name.lower()
and old_extrapolation_mode != ExtrapolationMode.SERVER_WEIGHTED.name.lower()
):
return True
if (
new_extrapolation_mode == ExtrapolationMode.NONE.name.lower()
and old_extrapolation_mode != ExtrapolationMode.NONE.name.lower()
):
return True
if (
new_extrapolation_mode == ExtrapolationMode.CLIENT_AND_SERVER_WEIGHTED.name.lower()
or new_extrapolation_mode == ExtrapolationMode.UNKNOWN.name.lower()
):
return False
return False
Copy link
Member

Choose a reason for hiding this comment

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

I think we don't need to be super restrictive here. We should only guard against people trying to set serve weighted so we don't have any un-intended consequences.

Suggested change
if (
new_extrapolation_mode == ExtrapolationMode.SERVER_WEIGHTED.name.lower()
and old_extrapolation_mode != ExtrapolationMode.SERVER_WEIGHTED.name.lower()
):
return True
if (
new_extrapolation_mode == ExtrapolationMode.NONE.name.lower()
and old_extrapolation_mode != ExtrapolationMode.NONE.name.lower()
):
return True
if (
new_extrapolation_mode == ExtrapolationMode.CLIENT_AND_SERVER_WEIGHTED.name.lower()
or new_extrapolation_mode == ExtrapolationMode.UNKNOWN.name.lower()
):
return False
return False
if (
new_extrapolation_mode == ExtrapolationMode.SERVER_WEIGHTED.name.lower()
and old_extrapolation_mode != ExtrapolationMode.SERVER_WEIGHTED.name.lower()
):
return True
return False

@nikkikapadia nikkikapadia marked this pull request as ready for review November 10, 2025 15:46
@nikkikapadia nikkikapadia requested a review from a team as a code owner November 10, 2025 15:46

if self.is_editing_transaction_dataset(snuba_query, data_source):
raise serializers.ValidationError(
"Updates to transaction-based alerts is disabled, as we migrate to the span dataset. Create span-based alerts (dataset: events_analytics_platform) with the is_transaction:true filter instead."
Copy link

Choose a reason for hiding this comment

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

Bug: Type mismatch in extrapolation_mode validation allows bypass when integer values are sent, preventing server_weighted mode restriction.
Severity: CRITICAL | Confidence: 1.00

🔍 Detailed Analysis

A type mismatch occurs in the validation logic for extrapolation_mode in src/sentry/incidents/endpoints/organization_alert_rule_details.py (lines 135-144) and src/sentry/incidents/endpoints/organization_alert_rule_index.py (line 104). When extrapolation_mode is sent as an integer (e.g., 3) in the request data, it is compared against a string representation (e.g., "server_weighted"). This comparison always evaluates to false, silently bypassing the intended validation that prevents users from setting the server_weighted mode.

💡 Suggested Fix

Ensure new_extrapolation_mode is consistently converted to a string or integer before comparison, matching the type of ExtrapolationMode.SERVER_WEIGHTED.name.lower() or .value.

🤖 Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent.
Verify if this is a real issue. If it is, propose a fix; if not, explain why it's not
valid.

Location: src/sentry/incidents/metric_issue_detector.py#L224

Potential issue: A type mismatch occurs in the validation logic for `extrapolation_mode`
in `src/sentry/incidents/endpoints/organization_alert_rule_details.py` (lines 135-144)
and `src/sentry/incidents/endpoints/organization_alert_rule_index.py` (line 104). When
`extrapolation_mode` is sent as an integer (e.g., `3`) in the request data, it is
compared against a string representation (e.g., `"server_weighted"`). This comparison
always evaluates to false, silently bypassing the intended validation that prevents
users from setting the `server_weighted` mode.

Did we get this right? 👍 / 👎 to inform future reviews.

return Response(serialized_rule)


def _is_invalid_extrapolation_mode(old_extrapolation_mode, new_extrapolation_mode) -> bool:
Copy link
Member

Choose a reason for hiding this comment

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

Seems like you could combine this and the one in metric_issue_detector.py since they're doing the same thing

Copy link
Member Author

Choose a reason for hiding this comment

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

ooop yes good point

)

if self.is_invalid_extrapolation_mode(snuba_query, data_source):
raise serializers.ValidationError("Invalid extrapolation mode for this alert type.")
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
raise serializers.ValidationError("Invalid extrapolation mode for this alert type.")
raise serializers.ValidationError("Invalid extrapolation mode for this detector type.")

@@ -617,3 +618,74 @@ def test_transaction_dataset_deprecation_generic_metrics_update(self) -> None:
with_feature("organizations:discover-saved-queries-deprecation"),
):
update_validator.save()

def test_invalid_extrapolation_mode_create(self) -> None:
Copy link
Member

Choose a reason for hiding this comment

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

Should a valid extrapolation mode be added to any of the existing tests? This isn't live yet but I have an open PR with a simple valid update test:

def test_update_with_valid_data(self) -> None:

Copy link
Member Author

Choose a reason for hiding this comment

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

we have a default in place if there is no explicit extrapolation mode passed so it's not necessary to be passed in 😌

partial=True,
)
if validator.is_valid():
if data.get("dataset") == Dataset.EventsAnalyticsPlatform.value:
Copy link
Contributor

Choose a reason for hiding this comment

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

Bug: Extrapolation Mode Validation Bypass

The validation checks data.get("dataset") from the request instead of checking the actual alert rule's dataset (alert_rule.snuba_query.dataset). This allows bypassing the extrapolation mode validation during partial updates by omitting the dataset field, enabling users to set server_weighted on EAP alerts.

Fix in Cursor Fix in Web

new_extrapolation_mode = data_source.get(
"extrapolation_mode", snuba_query.extrapolation_mode
)
if data_source.get("dataset") == Dataset.EventsAnalyticsPlatform:
Copy link
Contributor

Choose a reason for hiding this comment

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

Bug: Data Validation Flaw Allows Bypass

The validation checks data_source.get("dataset") from the request instead of the final dataset value. This allows bypassing extrapolation mode validation during partial updates where dataset is omitted, enabling server_weighted to be set on EAP detectors.

Fix in Cursor Fix in Web

)

if data.get("extrapolation_mode") == ExtrapolationMode.SERVER_WEIGHTED.name.lower():
raise ValidationError("server_weighted extrapolation mode is not supported for new alerts.")
Copy link
Contributor

Choose a reason for hiding this comment

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

Bug: Extrapolation Validation: Type Mismatch Bug

The validation compares extrapolation_mode against ExtrapolationMode.SERVER_WEIGHTED.name.lower() (a string), but the serializer field is IntegerField, so users send integers. This check will never trigger - it should compare against ExtrapolationMode.SERVER_WEIGHTED.value instead, matching the pattern used in _validate_extrapolation_mode.

Fix in Cursor Fix in Web

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Scope: Backend Automatically applied to PRs that change backend components

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants