From c72ccaee881ba300957c137d455533ce2b63ec79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonatan=20K=C5=82osko?= Date: Mon, 16 Sep 2024 11:57:58 +0200 Subject: [PATCH] Fix validation when opening notebook from file:// URL (#2783) --- lib/livebook/utils.ex | 36 +++++++++++++++---- .../live/open_live/url_component.ex | 2 +- 2 files changed, 30 insertions(+), 8 deletions(-) diff --git a/lib/livebook/utils.ex b/lib/livebook/utils.ex index 2401124e7..8cfdb2883 100644 --- a/lib/livebook/utils.ex +++ b/lib/livebook/utils.ex @@ -207,6 +207,11 @@ defmodule Livebook.Utils do @doc """ Validates if the given URL is syntactically valid. + ## Options + + * `:allow_file_scheme` - also accepts `file://` URLs. Defaults to + `flase` + ## Examples iex> Livebook.Utils.valid_url?("not_a_url") @@ -221,22 +226,39 @@ defmodule Livebook.Utils do iex> Livebook.Utils.valid_url?("http://") false + iex> Livebook.Utils.valid_url?("file:///tmp/test") + false + + iex> Livebook.Utils.valid_url?("file:///tmp/test", allow_file_scheme: true) + true + """ - @spec valid_url?(String.t()) :: boolean() - def valid_url?(url) do + @spec valid_url?(String.t(), keyword()) :: boolean() + def valid_url?(url, opts \\ []) do + opts = Keyword.validate!(opts, allow_file_scheme: false) + allow_file_scheme = opts[:allow_file_scheme] + case URI.new(url) do - {:ok, uri} -> uri.scheme != nil and uri.host not in [nil, ""] - {:error, _} -> false + {:ok, uri} when uri.scheme in ["http", "https"] -> + uri.host not in [nil, ""] + + {:ok, uri} when allow_file_scheme and uri.scheme == "file" -> + String.starts_with?(url, "file://") and uri.path not in [nil, ""] + + _ -> + false end end @doc """ Validates a change is a valid URL. + + See `valid_url?/2` for valid options. """ - @spec validate_url(Ecto.Changeset.t(), atom()) :: Ecto.Changeset.t() - def validate_url(changeset, field) do + @spec validate_url(Ecto.Changeset.t(), atom(), keyword()) :: Ecto.Changeset.t() + def validate_url(changeset, field, opts \\ []) do Ecto.Changeset.validate_change(changeset, field, fn ^field, url -> - if valid_url?(url) do + if valid_url?(url, opts) do [] else [{field, "must be a valid URL"}] diff --git a/lib/livebook_web/live/open_live/url_component.ex b/lib/livebook_web/live/open_live/url_component.ex index 6f0a975a1..9cae4da37 100644 --- a/lib/livebook_web/live/open_live/url_component.ex +++ b/lib/livebook_web/live/open_live/url_component.ex @@ -16,7 +16,7 @@ defmodule LivebookWeb.OpenLive.UrlComponent do cast({data, types}, attrs, [:url]) |> validate_required([:url]) - |> Livebook.Utils.validate_url(:url) + |> Livebook.Utils.validate_url(:url, allow_file_scheme: true) |> Livebook.Utils.validate_not_s3_url( :url, ~s{invalid s3:// URL scheme, you must first connect to the Cloud Storage in your Workspace page and then choose the relevant file in "From storage"}