Ensure recursive file/path ops raise on paths with null bytes

This commit is contained in:
José Valim
2017-07-20 11:13:52 +02:00
parent fa574e65bd
commit d5467a90a7
4 changed files with 45 additions and 10 deletions
+23 -5
View File
@@ -670,15 +670,21 @@ defmodule File do
File.cp_r "samples", "tmp"
# Same as before, but asks the user how to proceed in case of conflicts
File.cp_r "samples", "tmp", fn(source, destination) ->
File.cp_r "samples", "tmp", fn source, destination ->
IO.gets("Overwriting #{destination} by #{source}. Type y to confirm. ") == "y\n"
end
"""
@spec cp_r(Path.t, Path.t, (Path.t, Path.t -> boolean)) :: {:ok, [binary]} | {:error, posix, binary}
def cp_r(source, destination, callback \\ fn(_, _) -> true end) when is_function(callback, 2) do
source = IO.chardata_to_string(source)
destination = IO.chardata_to_string(destination)
def cp_r(source, destination, callback \\ fn _, _ -> true end) when is_function(callback, 2) do
source =
source
|> IO.chardata_to_string()
|> assert_no_null_byte!("File.cp_r/3")
destination =
destination
|> IO.chardata_to_string()
|> assert_no_null_byte!("File.cp_r/3")
case do_cp_r(source, destination, callback, []) do
{:error, _, _} = error -> error
@@ -947,7 +953,10 @@ defmodule File do
"""
@spec rm_rf(Path.t) :: {:ok, [binary]} | {:error, posix, binary}
def rm_rf(path) do
do_rm_rf(IO.chardata_to_string(path), {:ok, []})
path
|> IO.chardata_to_string()
|> assert_no_null_byte!("File.cp_r/3")
|> do_rm_rf({:ok, []})
end
defp do_rm_rf(path, {:ok, _} = entry) do
@@ -1454,6 +1463,15 @@ defmodule File do
@read_ahead_size 64 * 1024
defp assert_no_null_byte!(binary, operation) do
case :binary.match(binary, "\0") do
{_, _} ->
raise ArgumentError, "cannot execute #{operation} for path with null byte, got: #{inspect binary}"
:nomatch ->
binary
end
end
defp normalize_modes([:utf8 | rest], binary?) do
[encoding: :utf8] ++ normalize_modes(rest, binary?)
end
+9 -5
View File
@@ -615,17 +615,19 @@ defmodule Path do
def wildcard(glob, opts \\ []) do
mod = if Keyword.get(opts, :match_dot), do: :file, else: Path.Wildcard
glob
|> chardata_to_list()
|> chardata_to_list!()
|> :filelib.wildcard(mod)
|> Enum.map(&IO.chardata_to_string/1)
end
# expand_dot the given path by expanding "..", "." and "~".
defp chardata_to_list(chardata) do
defp chardata_to_list!(chardata) do
case :unicode.characters_to_list(chardata) do
result when is_list(result) ->
result
if 0 in result do
raise ArgumentError, "cannot execute Path.wildcard/2 for path with null byte, got: #{inspect chardata}"
else
result
end
{:error, encoded, rest} ->
raise UnicodeConversionError, encoded: encoded, rest: rest, kind: :invalid
@@ -654,6 +656,8 @@ defmodule Path do
end
end
# expand_dot the given path by expanding "..", "." and "~".
defp expand_dot(<<"/", rest::binary>>),
do: "/" <> do_expand_dot(rest)
defp expand_dot(<<letter, ":/", rest::binary>>) when letter in ?a..?z,
+9
View File
@@ -460,6 +460,11 @@ defmodule FileTest do
end
end
test "cp_r raises on path with null byte" do
assert_raise ArgumentError, ~r/null byte/, fn -> File.cp_r("source", "foo\0bar") end
assert_raise ArgumentError, ~r/null byte/, fn -> File.cp_r("foo\0bar", "dest") end
end
test "cp_r with src file and dest file" do
src = fixture_path("file.txt")
dest = tmp_path("sample.txt")
@@ -1113,6 +1118,10 @@ defmodule FileTest do
refute File.exists?(fixture)
end
test "rm_rf raises on path with null byte" do
assert_raise ArgumentError, ~r/null byte/, fn -> File.rm_rf("foo\0bar") end
end
test "rm_rf with symlink" do
from = tmp_path("tmp/from")
to = tmp_path("tmp/to")
+4
View File
@@ -32,6 +32,10 @@ defmodule PathTest do
File.rm_rf tmp_path("wildcard")
end
test "wildcard/2 raises on null byte" do
assert_raise ArgumentError, ~r/null byte/, fn -> Path.wildcard("foo\0bar") end
end
if windows?() do
describe "Windows" do
test "relative/1" do