diff --git a/CHANGELOG.md b/CHANGELOG.md index a65857c5..3ed5c8d8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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). +## v1.3.47 (2023-10-25) + +### 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 diff --git a/docs/make-docs b/docs/make-docs index 7d1486f7..a6cc6b1b 100755 --- a/docs/make-docs +++ b/docs/make-docs @@ -275,7 +275,7 @@ PATHS_mimir='docs/sources/mimir' PATHS_plugins_grafana_jira_datasource='docs/sources' PATHS_plugins_grafana_mongodb_datasource='docs/sources' PATHS_plugins_grafana_splunk_datasource='docs/sources' -Paths_tempo='docs/sources/tempo' +PATHS_tempo='docs/sources/tempo' PATHS_website='content' # identifier STR diff --git a/docs/sources/open-source/_index.md b/docs/sources/open-source/_index.md index 011b3058..8f9edf46 100644 --- a/docs/sources/open-source/_index.md +++ b/docs/sources/open-source/_index.md @@ -213,21 +213,22 @@ Grafana OnCall supports Twilio SMS and phone call notifications delivery. If you notifications using Twilio, complete the following steps: 1. Set `GRAFANA_CLOUD_NOTIFICATIONS_ENABLED` as **False** to ensure the Grafana OSS <-> Cloud connector is disabled. -1. From your **OnCall** environment, select **Env Variables** and configure all variables starting with `TWILIO_`. +2. From your **OnCall** environment, select **Env Variables** and configure all variables starting with `TWILIO_`. ### Zvonok.com Grafana OnCall supports Zvonok.com phone call notifications delivery. To configure phone call notifications using Zvonok.com, complete the following steps: -1. Change `PHONE_PROVIDER` value to `zvonok`. -2. Create a public API key on the Profile->Settings page, and assign its value to `ZVONOK_API_KEY`. -3. Create campaign and assign its ID value to `ZVONOK_CAMPAIGN_ID`. -4. If you are planning to use pre-recorded audio instead of a speech synthesizer, you can copy the ID of the audio clip +1. Set `GRAFANA_CLOUD_NOTIFICATIONS_ENABLED` as **False** to ensure the Grafana OSS <-> Cloud connector is disabled. +2. Change `PHONE_PROVIDER` value to `zvonok`. +3. Create a public API key on the Profile->Settings page, and assign its value to `ZVONOK_API_KEY`. +4. Create campaign and assign its ID value to `ZVONOK_CAMPAIGN_ID`. +5. If you are planning to use pre-recorded audio instead of a speech synthesizer, you can copy the ID of the audio clip to the variable `ZVONOK_AUDIO_ID` (optional step). -5. To make a call with a specific voice, you can set the `ZVONOK_SPEAKER_ID`. +6. To make a call with a specific voice, you can set the `ZVONOK_SPEAKER_ID`. By default, the ID used is `Salli` (optional step). -6. To process the call status, it is required to add a postback with the GET/POST method on the side of the zvonok.com +7. To process the call status, it is required to add a postback with the GET/POST method on the side of the zvonok.com service with the following format (optional step): `${ONCALL_BASE_URL}/zvonok/call_status_events?campaign_id={ct_campaign_id}&call_id={ct_call_id}&status={ct_status}&user_choice={ct_user_choice}` diff --git a/engine/apps/alerts/incident_appearance/renderers/slack_renderer.py b/engine/apps/alerts/incident_appearance/renderers/slack_renderer.py index 21d85260..2cb7ced3 100644 --- a/engine/apps/alerts/incident_appearance/renderers/slack_renderer.py +++ b/engine/apps/alerts/incident_appearance/renderers/slack_renderer.py @@ -437,6 +437,8 @@ class AlertGroupSlackRenderer(AlertGroupBaseRenderer): data = { "organization_id": self.alert_group.channel.organization_id, "alert_group_pk": self.alert_group.pk, + # eventually replace using alert_group_pk with alert_group_public_pk in slack payload + "alert_group_ppk": self.alert_group.public_primary_key, **kwargs, } return json.dumps(data) # Slack block elements allow to pass value as string only (max 2000 chars) diff --git a/engine/apps/alerts/models/channel_filter.py b/engine/apps/alerts/models/channel_filter.py index 2a9afdfe..40256045 100644 --- a/engine/apps/alerts/models/channel_filter.py +++ b/engine/apps/alerts/models/channel_filter.py @@ -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 diff --git a/engine/apps/api/serializers/channel_filter.py b/engine/apps/api/serializers/channel_filter.py index 17e92bbe..b9239d6d 100644 --- a/engine/apps/api/serializers/channel_filter.py +++ b/engine/apps/api/serializers/channel_filter.py @@ -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 diff --git a/engine/apps/api/tests/test_channel_filter.py b/engine/apps/api/tests/test_channel_filter.py index 31ab0a27..2fd0944d 100644 --- a/engine/apps/api/tests/test_channel_filter.py +++ b/engine/apps/api/tests/test_channel_filter.py @@ -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] diff --git a/engine/apps/public_api/serializers/routes.py b/engine/apps/public_api/serializers/routes.py index d680acf5..1907c9e9 100644 --- a/engine/apps/public_api/serializers/routes.py +++ b/engine/apps/public_api/serializers/routes.py @@ -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}) diff --git a/engine/apps/public_api/tests/test_routes.py b/engine/apps/public_api/tests/test_routes.py index 6aa8cded..a03a238b 100644 --- a/engine/apps/public_api/tests/test_routes.py +++ b/engine/apps/public_api/tests/test_routes.py @@ -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] diff --git a/engine/apps/slack/scenarios/step_mixins.py b/engine/apps/slack/scenarios/step_mixins.py index 46b23b1a..f6c30dcc 100644 --- a/engine/apps/slack/scenarios/step_mixins.py +++ b/engine/apps/slack/scenarios/step_mixins.py @@ -98,12 +98,27 @@ class AlertGroupActionsMixin: except (TypeError, json.JSONDecodeError): return None + # New slack messages from OnCall contain alert group primary key + try: + alert_group_ppk = value["alert_group_ppk"] + return AlertGroup.objects.get(public_primary_key=alert_group_ppk) + except (KeyError, TypeError): + pass + try: alert_group_pk = value["alert_group_pk"] + organization_pk = value["organization_id"] except (KeyError, TypeError): return None - return AlertGroup.objects.get(pk=alert_group_pk) + try: + # check organization as well for cases when the organization was migrated from "us" cluster to "eu" and + # slack message has an outdated payload with incorrect alert group id + alert_group = AlertGroup.objects.get(pk=alert_group_pk, channel__organization_id=organization_pk) + except AlertGroup.DoesNotExist: + return None + + return alert_group def _get_alert_group_from_message(self, payload: EventPayload) -> AlertGroup | None: """ @@ -128,12 +143,27 @@ class AlertGroupActionsMixin: except (TypeError, json.JSONDecodeError): continue + # New slack messages from OnCall contain alert group primary key + try: + alert_group_ppk = value["alert_group_ppk"] + return AlertGroup.objects.get(public_primary_key=alert_group_ppk) + except (KeyError, TypeError): + pass + try: alert_group_pk = value["alert_group_pk"] + organization_pk = value["organization_id"] except (KeyError, TypeError): continue - return AlertGroup.objects.get(pk=alert_group_pk) + try: + # check the organization as well for cases organization was migrated from "us" cluster to "eu" and + # the slack message has an outdated payload with incorrect alert group id + alert_group = AlertGroup.objects.get(pk=alert_group_pk, channel__organization_id=organization_pk) + except AlertGroup.DoesNotExist: + return None + + return alert_group return None def _get_alert_group_from_slack_message_in_db( diff --git a/engine/apps/slack/tests/test_scenario_steps/test_alert_group_actions.py b/engine/apps/slack/tests/test_scenario_steps/test_alert_group_actions.py index 0732f6ff..64473c9f 100644 --- a/engine/apps/slack/tests/test_scenario_steps/test_alert_group_actions.py +++ b/engine/apps/slack/tests/test_scenario_steps/test_alert_group_actions.py @@ -191,7 +191,21 @@ def test_get_alert_group_from_message( ], "message": { "ts": "RANDOM_MESSAGE_TS", - "attachments": [{"blocks": [{"elements": [{"value": json.dumps({"alert_group_pk": alert_group.pk})}]}]}], + "attachments": [ + { + "blocks": [ + { + "elements": [ + { + "value": json.dumps( + {"alert_group_pk": alert_group.pk, "organization_id": organization.pk} + ) + } + ] + } + ] + } + ], }, "channel": {"id": "RANDOM_CHANNEL_ID"}, } diff --git a/engine/apps/slack/tests/test_scenario_steps/test_manage_responders.py b/engine/apps/slack/tests/test_scenario_steps/test_manage_responders.py index 67bfbb4a..da707823 100644 --- a/engine/apps/slack/tests/test_scenario_steps/test_manage_responders.py +++ b/engine/apps/slack/tests/test_scenario_steps/test_manage_responders.py @@ -81,18 +81,16 @@ def manage_responders_setup( @pytest.mark.django_db def test_initial_state(manage_responders_setup): + organization, user, slack_team_identity, slack_user_identity = manage_responders_setup payload = { "trigger_id": TRIGGER_ID, "actions": [ { "type": "button", - "value": json.dumps({"organization_id": ORGANIZATION_ID, "alert_group_pk": ALERT_GROUP_ID}), + "value": json.dumps({"organization_id": organization.pk, "alert_group_pk": ALERT_GROUP_ID}), } ], } - - organization, user, slack_team_identity, slack_user_identity = manage_responders_setup - step = StartManageResponders(slack_team_identity, organization, user) with patch.object(step._slack_client, "views_open") as mock_slack_api_call: step.process_scenario(slack_user_identity, slack_team_identity, payload) diff --git a/engine/apps/slack/tests/test_slack_renderer.py b/engine/apps/slack/tests/test_slack_renderer.py index 08531c1e..2d60685d 100644 --- a/engine/apps/slack/tests/test_slack_renderer.py +++ b/engine/apps/slack/tests/test_slack_renderer.py @@ -17,7 +17,11 @@ def test_slack_renderer_acknowledge_button(make_organization, make_alert_receive button = elements[0] assert button["text"]["text"] == "Acknowledge" - assert json.loads(button["value"]) == {"organization_id": organization.pk, "alert_group_pk": alert_group.pk} + assert json.loads(button["value"]) == { + "organization_id": organization.pk, + "alert_group_pk": alert_group.pk, + "alert_group_ppk": alert_group.public_primary_key, + } @pytest.mark.django_db @@ -33,7 +37,11 @@ def test_slack_renderer_unacknowledge_button( button = elements[0] assert button["text"]["text"] == "Unacknowledge" - assert json.loads(button["value"]) == {"organization_id": organization.pk, "alert_group_pk": alert_group.pk} + assert json.loads(button["value"]) == { + "organization_id": organization.pk, + "alert_group_pk": alert_group.pk, + "alert_group_ppk": alert_group.public_primary_key, + } @pytest.mark.django_db @@ -47,7 +55,11 @@ def test_slack_renderer_resolve_button(make_organization, make_alert_receive_cha button = elements[1] assert button["text"]["text"] == "Resolve" - assert json.loads(button["value"]) == {"organization_id": organization.pk, "alert_group_pk": alert_group.pk} + assert json.loads(button["value"]) == { + "organization_id": organization.pk, + "alert_group_pk": alert_group.pk, + "alert_group_ppk": alert_group.public_primary_key, + } @pytest.mark.django_db @@ -61,7 +73,11 @@ def test_slack_renderer_unresolve_button(make_organization, make_alert_receive_c button = elements[0] assert button["text"]["text"] == "Unresolve" - assert json.loads(button["value"]) == {"organization_id": organization.pk, "alert_group_pk": alert_group.pk} + assert json.loads(button["value"]) == { + "organization_id": organization.pk, + "alert_group_pk": alert_group.pk, + "alert_group_ppk": alert_group.public_primary_key, + } @pytest.mark.django_db @@ -96,6 +112,7 @@ def test_slack_renderer_stop_invite_button( assert json.loads(action["value"]) == { "organization_id": organization.pk, "alert_group_pk": alert_group.pk, + "alert_group_ppk": alert_group.public_primary_key, "invitation_id": invitation.pk, } @@ -114,7 +131,12 @@ def test_slack_renderer_silence_button(make_organization, make_alert_receive_cha values = [json.loads(option["value"]) for option in button["options"]] assert values == [ - {"organization_id": organization.pk, "alert_group_pk": alert_group.pk, "delay": delay} + { + "organization_id": organization.pk, + "alert_group_pk": alert_group.pk, + "alert_group_ppk": alert_group.public_primary_key, + "delay": delay, + } for delay, _ in AlertGroup.SILENCE_DELAY_OPTIONS ] @@ -133,6 +155,7 @@ def test_slack_renderer_unsilence_button(make_organization, make_alert_receive_c assert json.loads(button["value"]) == { "organization_id": organization.pk, "alert_group_pk": alert_group.pk, + "alert_group_ppk": alert_group.public_primary_key, } @@ -150,6 +173,7 @@ def test_slack_renderer_attach_button(make_organization, make_alert_receive_chan assert json.loads(button["value"]) == { "organization_id": organization.pk, "alert_group_pk": alert_group.pk, + "alert_group_ppk": alert_group.public_primary_key, } @@ -170,6 +194,7 @@ def test_slack_renderer_unattach_button(make_organization, make_alert_receive_ch assert json.loads(action["value"]) == { "organization_id": organization.pk, "alert_group_pk": alert_group.pk, + "alert_group_ppk": alert_group.public_primary_key, } @@ -186,7 +211,11 @@ def test_slack_renderer_format_alert_button( button = elements[5] assert button["text"]["text"] == ":mag: Format Alert" - assert json.loads(button["value"]) == {"organization_id": organization.pk, "alert_group_pk": alert_group.pk} + assert json.loads(button["value"]) == { + "organization_id": organization.pk, + "alert_group_pk": alert_group.pk, + "alert_group_ppk": alert_group.public_primary_key, + } @pytest.mark.django_db @@ -205,5 +234,6 @@ def test_slack_renderer_resolution_notes_button( assert json.loads(button["value"]) == { "organization_id": organization.pk, "alert_group_pk": alert_group.pk, + "alert_group_ppk": alert_group.public_primary_key, "resolution_note_window_action": "edit", } diff --git a/engine/requirements.txt b/engine/requirements.txt index 3afcd382..f39f828e 100644 --- a/engine/requirements.txt +++ b/engine/requirements.txt @@ -53,7 +53,7 @@ django-anymail==8.6 django-deprecate-fields==0.1.1 pymdown-extensions==10.0 requests==2.31.0 -urllib3==1.26.17 +urllib3==1.26.18 prometheus_client==0.16.0 lxml==4.9.2 babel==2.12.1 diff --git a/grafana-plugin/yarn.lock b/grafana-plugin/yarn.lock index 7c049040..527a152c 100644 --- a/grafana-plugin/yarn.lock +++ b/grafana-plugin/yarn.lock @@ -22,6 +22,14 @@ dependencies: "@babel/highlight" "^7.18.6" +"@babel/code-frame@^7.22.13": + version "7.22.13" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.22.13.tgz#e3c1c099402598483b7a8c46a721d1038803755e" + integrity sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w== + dependencies: + "@babel/highlight" "^7.22.13" + chalk "^2.4.2" + "@babel/compat-data@^7.17.7", "@babel/compat-data@^7.18.8", "@babel/compat-data@^7.20.0", "@babel/compat-data@^7.20.1": version "7.20.1" resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.20.1.tgz#f2e6ef7790d8c8dbf03d379502dcc246dcce0b30" @@ -69,7 +77,7 @@ json5 "^2.2.1" semver "^6.3.0" -"@babel/generator@^7.18.9", "@babel/generator@^7.20.1", "@babel/generator@^7.20.2", "@babel/generator@^7.7.2": +"@babel/generator@^7.18.9", "@babel/generator@^7.20.2", "@babel/generator@^7.7.2": version "7.20.4" resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.20.4.tgz#4d9f8f0c30be75fd90a0562099a26e5839602ab8" integrity sha512-luCf7yk/cm7yab6CAW1aiFnmEfBJplb/JojV56MYEK7ziWfGmFlTfmL9Ehwfy4gFhbjBfWO1wj7/TuSbVNEEtA== @@ -78,6 +86,16 @@ "@jridgewell/gen-mapping" "^0.3.2" jsesc "^2.5.1" +"@babel/generator@^7.23.0": + version "7.23.0" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.23.0.tgz#df5c386e2218be505b34837acbcb874d7a983420" + integrity sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g== + dependencies: + "@babel/types" "^7.23.0" + "@jridgewell/gen-mapping" "^0.3.2" + "@jridgewell/trace-mapping" "^0.3.17" + jsesc "^2.5.1" + "@babel/helper-annotate-as-pure@^7.18.6": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.18.6.tgz#eaa49f6f80d5a33f9a5dd2276e6d6e451be0a6bb" @@ -141,6 +159,11 @@ resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz#0c0cee9b35d2ca190478756865bb3528422f51be" integrity sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg== +"@babel/helper-environment-visitor@^7.22.20": + version "7.22.20" + resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz#96159db61d34a29dba454c959f5ae4a649ba9167" + integrity sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA== + "@babel/helper-explode-assignable-expression@^7.18.6": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.18.6.tgz#41f8228ef0a6f1a036b8dfdfec7ce94f9a6bc096" @@ -156,6 +179,14 @@ "@babel/template" "^7.18.10" "@babel/types" "^7.19.0" +"@babel/helper-function-name@^7.23.0": + version "7.23.0" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz#1f9a3cdbd5b2698a670c30d2735f9af95ed52759" + integrity sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw== + dependencies: + "@babel/template" "^7.22.15" + "@babel/types" "^7.23.0" + "@babel/helper-hoist-variables@^7.18.6": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz#d4d2c8fb4baeaa5c68b99cc8245c56554f926678" @@ -163,6 +194,13 @@ dependencies: "@babel/types" "^7.18.6" +"@babel/helper-hoist-variables@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz#c01a007dac05c085914e8fb652b339db50d823bb" + integrity sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw== + dependencies: + "@babel/types" "^7.22.5" + "@babel/helper-member-expression-to-functions@^7.18.9": version "7.18.9" resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.18.9.tgz#1531661e8375af843ad37ac692c132841e2fd815" @@ -245,16 +283,33 @@ dependencies: "@babel/types" "^7.18.6" +"@babel/helper-split-export-declaration@^7.22.6": + version "7.22.6" + resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz#322c61b7310c0997fe4c323955667f18fcefb91c" + integrity sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g== + dependencies: + "@babel/types" "^7.22.5" + "@babel/helper-string-parser@^7.19.4": version "7.19.4" resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz#38d3acb654b4701a9b77fb0615a96f775c3a9e63" integrity sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw== +"@babel/helper-string-parser@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz#533f36457a25814cf1df6488523ad547d784a99f" + integrity sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw== + "@babel/helper-validator-identifier@^7.18.6", "@babel/helper-validator-identifier@^7.19.1": version "7.19.1" resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz#7eea834cf32901ffdc1a7ee555e2f9c27e249ca2" integrity sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w== +"@babel/helper-validator-identifier@^7.22.20": + version "7.22.20" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz#c4ae002c61d2879e724581d96665583dbc1dc0e0" + integrity sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A== + "@babel/helper-validator-option@^7.18.6": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz#bf0d2b5a509b1f336099e4ff36e1a63aa5db4db8" @@ -288,11 +343,25 @@ chalk "^2.0.0" js-tokens "^4.0.0" -"@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.18.10", "@babel/parser@^7.18.9", "@babel/parser@^7.20.1", "@babel/parser@^7.20.2": +"@babel/highlight@^7.22.13": + version "7.22.20" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.22.20.tgz#4ca92b71d80554b01427815e06f2df965b9c1f54" + integrity sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg== + dependencies: + "@babel/helper-validator-identifier" "^7.22.20" + chalk "^2.4.2" + js-tokens "^4.0.0" + +"@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.18.10", "@babel/parser@^7.18.9", "@babel/parser@^7.20.2": version "7.20.3" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.20.3.tgz#5358cf62e380cf69efcb87a7bb922ff88bfac6e2" integrity sha512-OP/s5a94frIPXwjzEcv5S/tpQfc6XhxYUnmWpgdqMWGgYCuErA3SzozaRAMQgSZWKeTJxht9aWAkUY+0UzvOFg== +"@babel/parser@^7.22.15", "@babel/parser@^7.23.0": + version "7.23.0" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.23.0.tgz#da950e622420bf96ca0d0f2909cdddac3acd8719" + integrity sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw== + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.18.6": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.18.6.tgz#da5b8f9a580acdfbe53494dba45ea389fb09a4d2" @@ -1178,19 +1247,28 @@ "@babel/parser" "^7.18.10" "@babel/types" "^7.18.10" -"@babel/traverse@^7.18.9", "@babel/traverse@^7.19.0", "@babel/traverse@^7.19.1", "@babel/traverse@^7.20.1", "@babel/traverse@^7.7.2": - version "7.20.1" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.20.1.tgz#9b15ccbf882f6d107eeeecf263fbcdd208777ec8" - integrity sha512-d3tN8fkVJwFLkHkBN479SOsw4DMZnz8cdbL/gvuDuzy3TS6Nfw80HuQqhw1pITbIruHyh7d1fMA47kWzmcUEGA== +"@babel/template@^7.22.15": + version "7.22.15" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.22.15.tgz#09576efc3830f0430f4548ef971dde1350ef2f38" + integrity sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w== dependencies: - "@babel/code-frame" "^7.18.6" - "@babel/generator" "^7.20.1" - "@babel/helper-environment-visitor" "^7.18.9" - "@babel/helper-function-name" "^7.19.0" - "@babel/helper-hoist-variables" "^7.18.6" - "@babel/helper-split-export-declaration" "^7.18.6" - "@babel/parser" "^7.20.1" - "@babel/types" "^7.20.0" + "@babel/code-frame" "^7.22.13" + "@babel/parser" "^7.22.15" + "@babel/types" "^7.22.15" + +"@babel/traverse@^7.18.9", "@babel/traverse@^7.19.0", "@babel/traverse@^7.19.1", "@babel/traverse@^7.20.1", "@babel/traverse@^7.7.2": + version "7.23.2" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.23.2.tgz#329c7a06735e144a506bdb2cad0268b7f46f4ad8" + integrity sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw== + dependencies: + "@babel/code-frame" "^7.22.13" + "@babel/generator" "^7.23.0" + "@babel/helper-environment-visitor" "^7.22.20" + "@babel/helper-function-name" "^7.23.0" + "@babel/helper-hoist-variables" "^7.22.5" + "@babel/helper-split-export-declaration" "^7.22.6" + "@babel/parser" "^7.23.0" + "@babel/types" "^7.23.0" debug "^4.1.0" globals "^11.1.0" @@ -1203,6 +1281,15 @@ "@babel/helper-validator-identifier" "^7.19.1" to-fast-properties "^2.0.0" +"@babel/types@^7.22.15", "@babel/types@^7.22.5", "@babel/types@^7.23.0": + version "7.23.0" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.23.0.tgz#8c1f020c9df0e737e4e247c0619f58c68458aaeb" + integrity sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg== + dependencies: + "@babel/helper-string-parser" "^7.22.5" + "@babel/helper-validator-identifier" "^7.22.20" + to-fast-properties "^2.0.0" + "@bcoe/v8-coverage@^0.2.3": version "0.2.3" resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39"