diff --git a/lib/elixir/lib/path.ex b/lib/elixir/lib/path.ex index a72071b8574..81c509a8260 100644 --- a/lib/elixir/lib/path.ex +++ b/lib/elixir/lib/path.ex @@ -699,6 +699,32 @@ defmodule Path do end end + @doc """ + Safely joins two paths. + + Returns `{:ok, path}` if `right` is safe to append to `left`, or `:error` + otherwise. See `safe_relative/2` for the exact safety rules applied to `right`. + + ## Examples + + iex> Path.safe_join("foo", "bar") + {:ok, "foo/bar"} + + iex> Path.safe_join("foo", "../bar") + :error + + iex> Path.safe_join("foo", "/bar") + :error + + """ + @doc since: "1.21.0" + @spec safe_join(t, t) :: {:ok, t} | :error + def safe_join(left, right) do + with {:ok, right} <- safe_relative(right, left) do + {:ok, join(left, right)} + end + end + @doc ~S""" Splits the path into a list at the path separator. diff --git a/lib/elixir/test/elixir/path_test.exs b/lib/elixir/test/elixir/path_test.exs index aaed0fc0861..49206eb8666 100644 --- a/lib/elixir/test/elixir/path_test.exs +++ b/lib/elixir/test/elixir/path_test.exs @@ -416,6 +416,14 @@ defmodule PathTest do assert Path.join(["/foo", "bar"], ["fiz", "buz"]) == "/foobar/fizbuz" end + test "safe_join/2" do + assert {:ok, "foo/bar"} = Path.safe_join("foo", "bar") + assert {:ok, "foo"} = Path.safe_join("foo", ".") + + assert :error = Path.safe_join("foo", "../bar") + assert :error = Path.safe_join("foo", "/bar") + end + test "split/1" do assert Path.split("") == [] assert Path.split("foo") == ["foo"]