From 72586ea42ecc7507be1c0111b4cb3847b62bc0e4 Mon Sep 17 00:00:00 2001 From: Radek Janicki <13463966+rdk08@users.noreply.github.com> Date: Mon, 23 Oct 2023 09:56:20 +0200 Subject: [PATCH] Validate special props --- lib/plausible/event/clickhouse_schema.ex | 25 +++++++++++++++ lib/plausible/helpers/value.ex | 12 +++++++ lib/plausible/session/clickhouse_schema.ex | 25 +++++++++++++++ .../api/external_controller_test.exs | 31 +++++++++++++++++++ 4 files changed, 93 insertions(+) create mode 100644 lib/plausible/helpers/value.ex diff --git a/lib/plausible/event/clickhouse_schema.ex b/lib/plausible/event/clickhouse_schema.ex index 3dddf3b94499..8a31ebb12b05 100644 --- a/lib/plausible/event/clickhouse_schema.ex +++ b/lib/plausible/event/clickhouse_schema.ex @@ -1,7 +1,10 @@ defmodule Plausible.ClickhouseEvent do use Ecto.Schema + import Ecto.Changeset + alias Plausible.ValueHelpers + @primary_key false schema "events" do field :event_id, :integer @@ -89,5 +92,27 @@ defmodule Plausible.ClickhouseEvent do empty_values: [nil, ""] ) |> validate_required([:name, :domain, :hostname, :pathname, :event_id, :user_id, :timestamp]) + |> validate_campaign_id() + |> validate_product_id() + end + + defp validate_campaign_id(changeset) do + campaign_id = get_field(changeset, :campaign_id) + + if ValueHelpers.validate(campaign_id, type: :prefixed_id) do + changeset + else + delete_change(changeset, :campaign_id) + end + end + + defp validate_product_id(changeset) do + product_id = get_field(changeset, :product_id) + + if ValueHelpers.validate(product_id, type: :prefixed_id) do + changeset + else + delete_change(changeset, :product_id) + end end end diff --git a/lib/plausible/helpers/value.ex b/lib/plausible/helpers/value.ex new file mode 100644 index 000000000000..8f706bca46ec --- /dev/null +++ b/lib/plausible/helpers/value.ex @@ -0,0 +1,12 @@ +defmodule Plausible.ValueHelpers do + @spec validate(any(), keyword()) :: any() + def validate(value, type: :prefixed_id) when is_binary(value) do + prefixed_id_pattern = ~r/\A\w+-\d+\Z/ + + if Regex.match?(prefixed_id_pattern, value), do: value, else: nil + end + + def validate(nil, _), do: nil + def validate("", _), do: nil + def validate(value, _), do: value +end diff --git a/lib/plausible/session/clickhouse_schema.ex b/lib/plausible/session/clickhouse_schema.ex index 72948d3a7c79..7a9e42d72039 100644 --- a/lib/plausible/session/clickhouse_schema.ex +++ b/lib/plausible/session/clickhouse_schema.ex @@ -1,7 +1,10 @@ defmodule Plausible.ClickhouseSession do use Ecto.Schema + import Ecto.Changeset + alias Plausible.ValueHelpers + @primary_key false schema "sessions" do field :hostname, :string @@ -80,5 +83,27 @@ defmodule Plausible.ClickhouseSession do :screen_size ]) |> validate_required([:hostname, :domain, :fingerprint, :is_bounce, :start]) + |> validate_campaign_id() + |> validate_product_id() + end + + defp validate_campaign_id(changeset) do + campaign_id = get_field(changeset, :campaign_id) + + if ValueHelpers.validate(campaign_id, type: :prefixed_id) do + changeset + else + delete_change(changeset, :campaign_id) + end + end + + defp validate_product_id(changeset) do + product_id = get_field(changeset, :product_id) + + if ValueHelpers.validate(product_id, type: :prefixed_id) do + changeset + else + delete_change(changeset, :product_id) + end end end diff --git a/test/plausible_web/controllers/api/external_controller_test.exs b/test/plausible_web/controllers/api/external_controller_test.exs index 9bb3fe2a9617..170b38302604 100644 --- a/test/plausible_web/controllers/api/external_controller_test.exs +++ b/test/plausible_web/controllers/api/external_controller_test.exs @@ -527,6 +527,37 @@ defmodule PlausibleWeb.Api.ExternalControllerTest do assert Map.get(event, :"meta.value") == [] end + test "casts special props, invalidates specific params", %{conn: conn} do + params = %{ + name: "Signup", + url: "http://gigride.live/", + domain: "special-props-validation-test.com", + props: %{ + campaign_id: "12345", + company_id: 10, + page_id: 15, + product_id: "incorrect-product-id", + careers_application_form_uuid: "313a26c2-741c-421c-9a6b-f39c02c8d35c" + } + } + + conn + |> post("/api/event", params) + + event = get_event("special-props-validation-test.com") + + assert Map.get(event, :campaign_id) == "" + assert Map.get(event, :product_id) == "" + assert Map.get(event, :company_id) == 10 + assert Map.get(event, :page_id) == 15 + + assert Map.get(event, :careers_application_form_uuid) == + "313a26c2-741c-421c-9a6b-f39c02c8d35c" + + assert Map.get(event, :"meta.key") == [] + assert Map.get(event, :"meta.value") == [] + end + test "generates event id", %{conn: conn} do params = %{ name: "Signup",