From 8ccd8e6586eb59df63692c2a087cdf8c3bd5fc88 Mon Sep 17 00:00:00 2001 From: moon Date: Mon, 2 Dec 2024 07:37:54 -0500 Subject: [PATCH] better path validation --- lib/balls_pds/ecto/schema/object.ex | 30 ++++++++++++++++------ lib/balls_pds/util/util.ex | 39 +++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+), 7 deletions(-) create mode 100644 lib/balls_pds/util/util.ex diff --git a/lib/balls_pds/ecto/schema/object.ex b/lib/balls_pds/ecto/schema/object.ex index 6dc42f4..ff58309 100644 --- a/lib/balls_pds/ecto/schema/object.ex +++ b/lib/balls_pds/ecto/schema/object.ex @@ -31,26 +31,42 @@ defmodule BallsPDS.Ecto.Schema.Object do :path, :public ]) - |> validate_format(:path, ~r/^[^<>:"\\|\?\*\0]+$/, message: "Invalid characters") - |> validate_format(:path, ~r/^\/$|^\/.*[^\/]$/, - message: "Must have leading slash but no trailing slash" - ) - |> validate_format(:path, ~r/(? validate_path(:path) |> validate_format( :content_type, ~r/^(application|audio|font|image|message|model|multipart|text|video)\/[\w\-\+\.]+(?:;\s*charset=[\w\-]+)?$/i, allow_nil: true ) |> validate_format(:activitypub_type, ~r/[A-Z][a-zA-Z0-9]*/, allow_nil: true) - |> validate_format(:storage_key, ~r/^[0-9a-f]{64}$/, + |> validate_format(:storage_key, ~r/^[0-9a-f]+$/, allow_nil: true, - message: "Invalid 256-bit hexadecimal string" + message: "Invalid hexadecimal string" ) |> validate_inclusion(:public, [true, false]) |> validate_number(:total_items, greater_than: -1, allow_nil: true) |> unique_constraint(:path) end + defp validate_path(changeset, field) do + validate_change(changeset, field, fn _, path -> + case BallsPDS.Util.Util.is_valid_path?(path) do + :ok -> [] + + {:error, :invalid_char} -> + [{field, "Invalid characters"}] + + {:error, :trailing_or_no_starting_slash} -> + [{field, "Trailing slash, or no starting slash"}] + + {:error, :sequential_slashes} -> + [{field, "Sequential slashes"}] + + {:error, :relative_path} -> + [{field, "Relative path"}] + end + end) + end + def get_by_path(path) when is_binary(path) do query = from(o in __MODULE__, diff --git a/lib/balls_pds/util/util.ex b/lib/balls_pds/util/util.ex new file mode 100644 index 0000000..99dc831 --- /dev/null +++ b/lib/balls_pds/util/util.ex @@ -0,0 +1,39 @@ +defmodule BallsPDS.Util.Util do + @valid_path_chars_regex ~r/^[^<>:"\\|\?\*\0\s]+$/ + @starting_trailing_slashes_regex ~r/^\/$|^\/.*[^\/]$/ + @sequential_slashes_regex ~r/(? {:error, error} + end + end + + @doc """ + Every AP ID in an AP object should validate against this function. + """ + def is_valid_personal_ap_id(id) when is_binary(id) do + owner = Application.get_env(:balls_pds, :owner_ap_id) + service = Application.get_env(:balls_pds, :did_service) + prefix = "#{owner}?service=#{URI.encode(service)}&relativeRef=" + + with {:prefix, ^prefix <> relative_ref} <- {:prefix, id}, + {:decode, relative_ref} <- {:decode, URI.decode(relative_ref)}, + {:valid, :ok} <- {:valid, is_valid_path?(relative_ref)} do + true + else + _ -> false + end + end +end