diff --git a/CHANGELOG.md b/CHANGELOG.md index 2131f7cc4e..06c8f2ed4d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,11 @@ # Changelog +## 1.2.2 (2017-3-14) + +* Big Fixes + * [Controller] Harden local redirect against arbitrary URL redirection + ## 1.2.1 (2016-8-11) * Enhancements diff --git a/installer/lib/phoenix_new.ex b/installer/lib/phoenix_new.ex index da7190969c..0661bcfe62 100644 --- a/installer/lib/phoenix_new.ex +++ b/installer/lib/phoenix_new.ex @@ -491,7 +491,7 @@ defmodule Mix.Tasks.Phoenix.New do :crypto.strong_rand_bytes(length) |> Base.encode64 |> binary_part(0, length) end - defp phoenix_dep("deps/phoenix"), do: ~s[{:phoenix, "~> 1.2.1"}] + defp phoenix_dep("deps/phoenix"), do: ~s[{:phoenix, "~> 1.2.2"}] # defp phoenix_dep("deps/phoenix"), do: ~s[{:phoenix, github: "phoenixframework/phoenix", override: true}] defp phoenix_dep(path), do: ~s[{:phoenix, path: #{inspect path}, override: true}] diff --git a/installer/mix.exs b/installer/mix.exs index 6dc5b908d1..4f7a17c0ac 100644 --- a/installer/mix.exs +++ b/installer/mix.exs @@ -3,7 +3,7 @@ defmodule Phoenix.New.Mixfile do def project do [app: :phoenix_new, - version: "1.2.1", + version: "1.2.2", elixir: "~> 1.2"] end diff --git a/lib/phoenix/controller.ex b/lib/phoenix/controller.ex index c0a09defb2..8b04be7bff 100644 --- a/lib/phoenix/controller.ex +++ b/lib/phoenix/controller.ex @@ -306,18 +306,22 @@ defmodule Phoenix.Controller do defp url(opts) do cond do - to = opts[:to] -> - case to do - "//" <> _ -> raise_invalid_url(to) - "/" <> _ -> to - _ -> raise_invalid_url(to) - end - external = opts[:external] -> - external - true -> - raise ArgumentError, "expected :to or :external option in redirect/2" + to = opts[:to] -> validate_local_url(to) + external = opts[:external] -> external + true -> raise ArgumentError, "expected :to or :external option in redirect/2" end end + @invalid_local_url_chars ["\\"] + defp validate_local_url("//" <> _ = to), do: raise_invalid_url(to) + defp validate_local_url("/" <> _ = to) do + if String.contains?(to, @invalid_local_url_chars) do + raise ArgumentError, "unsafe characters detected for local redirect in URL #{inspect to}" + else + to + end + end + defp validate_local_url(to), do: raise_invalid_url(to) + @spec raise_invalid_url(term()) :: no_return() defp raise_invalid_url(url) do raise ArgumentError, "the :to option in redirect expects a path but was #{inspect url}" diff --git a/mix.exs b/mix.exs index 4af3ff4702..bce21bb762 100644 --- a/mix.exs +++ b/mix.exs @@ -1,7 +1,7 @@ defmodule Phoenix.Mixfile do use Mix.Project - @version "1.2.1" + @version "1.2.2" def project do [app: :phoenix, diff --git a/mix.lock b/mix.lock index d33724672e..cec317a285 100644 --- a/mix.lock +++ b/mix.lock @@ -1,4 +1,4 @@ -%{"cowboy": {:hex, :cowboy, "1.0.4", "a324a8df9f2316c833a470d918aaf73ae894278b8aa6226ce7a9bf699388f878", [:rebar, :make], [{:cowlib, "~> 1.0.0", [hex: :cowlib, optional: false]}, {:ranch, "~> 1.0", [hex: :ranch, optional: false]}]}, +%{"cowboy": {:hex, :cowboy, "1.0.4", "a324a8df9f2316c833a470d918aaf73ae894278b8aa6226ce7a9bf699388f878", [:make, :rebar], [{:cowlib, "~> 1.0.0", [hex: :cowlib, optional: false]}, {:ranch, "~> 1.0", [hex: :ranch, optional: false]}]}, "cowlib": {:hex, :cowlib, "1.0.2", "9d769a1d062c9c3ac753096f868ca121e2730b9a377de23dec0f7e08b1df84ee", [:make], []}, "earmark": {:hex, :earmark, "0.2.1", "ba6d26ceb16106d069b289df66751734802777a3cbb6787026dd800ffeb850f3", [:mix], []}, "ex_doc": {:hex, :ex_doc, "0.12.0", "b774aabfede4af31c0301aece12371cbd25995a21bb3d71d66f5c2fe074c603f", [:mix], [{:earmark, "~> 0.2", [hex: :earmark, optional: false]}]}, diff --git a/test/phoenix/controller/controller_test.exs b/test/phoenix/controller/controller_test.exs index 16f458cf0b..488dbe3ae9 100644 --- a/test/phoenix/controller/controller_test.exs +++ b/test/phoenix/controller/controller_test.exs @@ -239,6 +239,10 @@ defmodule Phoenix.Controller.ControllerTest do assert_raise ArgumentError, ~r/the :to option in redirect expects a path/, fn -> redirect(conn(:get, "/"), to: "//example.com") end + + assert_raise ArgumentError, ~r/unsafe/, fn -> + redirect(conn(:get, "/"), to: "/\\example.com") + end end test "redirect/2 with :external" do