Fix channel filter endpoint (#3192)

# What this PR does
Add filtering term length check for channel filter endpoints

## Which issue(s) this PR fixes

Closes https://github.com/grafana/oncall-private/issues/2114

## Checklist

- [x] Unit, integration, and e2e (if applicable) tests updated
- [x] Documentation added (or `pr:no public docs` PR label added if not
required)
- [x] `CHANGELOG.md` updated (or `pr:no changelog` PR label added if not
required)
This commit is contained in:
Yulya Artyukhina 2023-10-25 14:33:05 +02:00 committed by GitHub
parent 9991c3c899
commit a3aeb0ecb5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 94 additions and 4 deletions

View file

@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## Unreleased
### Fixed
- Add filtering term length check for channel filter endpoints @Ferril ([#3192](https://github.com/grafana/oncall/pull/3192))
## v1.3.46 (2023-10-23)
### Added

View file

@ -76,7 +76,9 @@ class ChannelFilter(OrderedModel):
notification_backends = models.JSONField(null=True, default=None)
created_at = models.DateTimeField(auto_now_add=True)
filtering_term = models.CharField(max_length=1024, null=True, default=None)
FILTERING_TERM_MAX_LENGTH = 1024
filtering_term = models.CharField(max_length=FILTERING_TERM_MAX_LENGTH, null=True, default=None)
FILTERING_TERM_TYPE_REGEX = 0
FILTERING_TERM_TYPE_JINJA2 = 1

View file

@ -60,6 +60,12 @@ class ChannelFilterSerializer(EagerLoadingMixin, serializers.ModelSerializer):
def validate(self, data):
filtering_term = data.get("filtering_term")
filtering_term_type = data.get("filtering_term_type")
if filtering_term is not None:
if len(filtering_term) > ChannelFilter.FILTERING_TERM_MAX_LENGTH:
raise serializers.ValidationError(
f"Expression is too long. Maximum length: {ChannelFilter.FILTERING_TERM_MAX_LENGTH} characters, "
f"current length: {len(filtering_term)}"
)
if filtering_term_type == ChannelFilter.FILTERING_TERM_TYPE_JINJA2:
try:
valid_jinja_template_for_serializer_method_field({"route_template": filtering_term})
@ -141,7 +147,6 @@ class ChannelFilterSerializer(EagerLoadingMixin, serializers.ModelSerializer):
class ChannelFilterCreateSerializer(ChannelFilterSerializer):
alert_receive_channel = OrganizationFilteredPrimaryKeyRelatedField(queryset=AlertReceiveChannel.objects)
slack_channel = serializers.CharField(allow_null=True, required=False, source="slack_channel_id")
filtering_term = serializers.CharField(required=False, allow_null=True, allow_blank=True)
class Meta:
model = ChannelFilter

View file

@ -552,3 +552,41 @@ def test_channel_filter_convert_from_regex_to_jinja2(
assert jinja2_channel_filter.filtering_term == final_filtering_term
# Check if the same alert is matched to the channel filter (route) new jinja2
assert bool(jinja2_channel_filter.is_satisfying(payload)) is True
@pytest.mark.django_db
def test_channel_filter_long_filtering_term(
make_organization_and_user_with_plugin_token,
make_alert_receive_channel,
make_escalation_chain,
make_channel_filter,
make_user_auth_headers,
):
organization, user, token = make_organization_and_user_with_plugin_token()
alert_receive_channel = make_alert_receive_channel(organization)
make_escalation_chain(organization)
make_channel_filter(alert_receive_channel, is_default=True)
client = APIClient()
long_filtering_term = "a" * (ChannelFilter.FILTERING_TERM_MAX_LENGTH + 1)
url = reverse("api-internal:channel_filter-list")
data_for_creation = {
"alert_receive_channel": alert_receive_channel.public_primary_key,
"filtering_term": long_filtering_term,
}
response = client.post(url, data=data_for_creation, format="json", **make_user_auth_headers(user, token))
assert response.status_code == status.HTTP_400_BAD_REQUEST
assert "Expression is too long" in response.json()["non_field_errors"][0]
channel_filter = make_channel_filter(alert_receive_channel, filtering_term="a", is_default=False)
url = reverse("api-internal:channel_filter-detail", kwargs={"pk": channel_filter.public_primary_key})
data_for_update = {
"filtering_term": long_filtering_term,
}
response = client.put(url, data=data_for_update, format="json", **make_user_auth_headers(user, token))
assert response.status_code == status.HTTP_400_BAD_REQUEST
assert "Expression is too long" in response.json()["non_field_errors"][0]

View file

@ -163,8 +163,14 @@ class ChannelFilterSerializer(BaseChannelFilterSerializer):
return super().create(validated_data)
def validate(self, data):
filtering_term = data.get("routing_regex")
filtering_term_type = data.get("routing_type")
filtering_term = data.get("filtering_term")
filtering_term_type = data.get("filtering_term_type")
if filtering_term is not None:
if len(filtering_term) > ChannelFilter.FILTERING_TERM_MAX_LENGTH:
raise serializers.ValidationError(
f"Expression is too long. Maximum length: {ChannelFilter.FILTERING_TERM_MAX_LENGTH} characters, "
f"current length: {len(filtering_term)}"
)
if filtering_term_type == ChannelFilter.FILTERING_TERM_TYPE_JINJA2:
try:
valid_jinja_template_for_serializer_method_field({"route_template": filtering_term})

View file

@ -455,3 +455,36 @@ def test_update_route_with_manual_ordering(
response = client.put(url, format="json", HTTP_AUTHORIZATION=token, data=data_to_update)
assert response.status_code == status.HTTP_400_BAD_REQUEST
@pytest.mark.django_db
def test_routes_long_filtering_term(
route_public_api_setup,
make_channel_filter,
):
organization, _, token, alert_receive_channel, escalation_chain, _ = route_public_api_setup
client = APIClient()
long_filtering_term = "a" * (ChannelFilter.FILTERING_TERM_MAX_LENGTH + 1)
url = reverse("api-public:routes-list")
data_for_creation = {
"integration_id": alert_receive_channel.public_primary_key,
"escalation_chain_id": escalation_chain.public_primary_key,
"routing_regex": long_filtering_term,
}
response = client.post(url, data=data_for_creation, format="json", HTTP_AUTHORIZATION=token)
assert response.status_code == status.HTTP_400_BAD_REQUEST
assert "Expression is too long" in response.json()["non_field_errors"][0]
channel_filter = make_channel_filter(alert_receive_channel, filtering_term="a", is_default=False)
url = reverse("api-public:routes-detail", kwargs={"pk": channel_filter.public_primary_key})
data_for_update = {
"routing_regex": long_filtering_term,
}
response = client.put(url, data=data_for_update, format="json", HTTP_AUTHORIZATION=token)
assert response.status_code == status.HTTP_400_BAD_REQUEST
assert "Expression is too long" in response.json()["non_field_errors"][0]