Browse Source

Escape paths and args in shell commands

Basic escaping is done only. Fancy hex sequences are not covered, but
this should otherwise take care of the most common issues.

Fixes #497
pull/621/head
Fred Hebert 9 years ago
parent
commit
87a57f0432
4 changed files with 72 additions and 39 deletions
  1. +18
    -19
      src/rebar_file_utils.erl
  2. +19
    -9
      src/rebar_git_resource.erl
  3. +19
    -10
      src/rebar_hg_resource.erl
  4. +16
    -1
      src/rebar_utils.erl

+ 18
- 19
src/rebar_file_utils.erl View File

@ -98,7 +98,8 @@ symlink_or_copy(Source, Target) ->
win32_symlink(Source, Target) ->
Res = rebar_utils:sh(
?FMT("cmd /c mklink /j \"~s\" \"~s\"",
[filename:nativename(Target), filename:nativename(Source)]),
[rebar_utils:escape_double_quotes(filename:nativename(Target)),
rebar_utils:escape_double_quotes(filename:nativename(Source))]),
[{use_stdout, false}, return_on_error]),
case win32_ok(Res) of
true -> ok;
@ -115,7 +116,7 @@ win32_symlink(Source, Target) ->
rm_rf(Target) ->
case os:type() of
{unix, _} ->
EscTarget = escape_path(Target),
EscTarget = rebar_utils:escape_chars(Target),
{ok, []} = rebar_utils:sh(?FMT("rm -rf ~s", [EscTarget]),
[{use_stdout, false}, abort_on_error]),
ok;
@ -134,10 +135,10 @@ cp_r([], _Dest) ->
cp_r(Sources, Dest) ->
case os:type() of
{unix, _} ->
EscSources = [escape_path(Src) || Src <- Sources],
EscSources = [rebar_utils:escape_chars(Src) || Src <- Sources],
SourceStr = string:join(EscSources, " "),
{ok, []} = rebar_utils:sh(?FMT("cp -R ~s \"~s\"",
[SourceStr, Dest]),
[SourceStr, rebar_utils:escape_double_quotes(Dest)]),
[{use_stdout, false}, abort_on_error]),
ok;
{win32, _} ->
@ -149,8 +150,8 @@ cp_r(Sources, Dest) ->
mv(Source, Dest) ->
case os:type() of
{unix, _} ->
EscSource = escape_path(Source),
EscDest = escape_path(Dest),
EscSource = rebar_utils:escape_chars(Source),
EscDest = rebar_utils:escape_chars(Dest),
{ok, []} = rebar_utils:sh(?FMT("mv ~s ~s", [EscSource, EscDest]),
[{use_stdout, false}, abort_on_error]),
ok;
@ -158,13 +159,13 @@ mv(Source, Dest) ->
Cmd = case filelib:is_dir(Source) of
true ->
?FMT("robocopy /move /s \"~s\" \"~s\" 1> nul",
[filename:nativename(Source),
filename:nativename(Dest)]);
[rebar_utils:escape_double_quotes(filename:nativename(Source)),
rebar_utils:escape_double_quotes(filename:nativename(Dest))]);
false ->
?FMT("robocopy /move /s \"~s\" \"~s\" \"~s\" 1> nul",
[filename:nativename(filename:dirname(Source)),
filename:nativename(Dest),
filename:basename(Source)])
[rebar_utils:escape_double_quotes(filename:nativename(filename:dirname(Source))),
rebar_utils:escape_double_quotes(filename:nativename(Dest)),
rebar_utils:escape_double_quotes(filename:basename(Source))])
end,
Res = rebar_utils:sh(Cmd,
[{use_stdout, false}, return_on_error]),
@ -250,7 +251,7 @@ touch(Path) ->
delete_each_dir_win32([]) -> ok;
delete_each_dir_win32([Dir | Rest]) ->
{ok, []} = rebar_utils:sh(?FMT("rd /q /s \"~s\"",
[filename:nativename(Dir)]),
[rebar_utils:escape_double_quotes(filename:nativename(Dir))]),
[{use_stdout, false}, return_on_error]),
delete_each_dir_win32(Rest).
@ -260,13 +261,13 @@ xcopy_win32(Source,Dest)->
Cmd = case filelib:is_dir(Source) of
true ->
?FMT("robocopy \"~s\" \"~s\" /e /is 1> nul",
[filename:nativename(Source),
filename:nativename(Dest)]);
[rebar_utils:escape_double_quotes(filename:nativename(Source)),
rebar_utils:escape_double_quotes(filename:nativename(Dest))]);
false ->
?FMT("robocopy \"~s\" \"~s\" \"~s\" /e /is 1> nul",
[filename:nativename(filename:dirname(Source)),
filename:nativename(Dest),
filename:basename(Source)])
[rebar_utils:escape_double_quotes(filename:nativename(filename:dirname(Source))),
rebar_utils:escape_double_quotes(filename:nativename(Dest)),
rebar_utils:escape_double_quotes(filename:basename(Source))])
end,
Res = rebar_utils:sh(Cmd,
[{use_stdout, false}, return_on_error]),
@ -318,5 +319,3 @@ cp_r_win32(Source,Dest) ->
end, filelib:wildcard(Source)),
ok.
escape_path(Str) ->
re:replace(Str, "([ ()?])", "\\\\&", [global, {return, list}]).

+ 19
- 9
src/rebar_git_resource.erl View File

@ -16,7 +16,7 @@ lock(AppDir, {git, Url, _}) ->
lock(AppDir, {git, Url}) ->
AbortMsg = io_lib:format("Locking of git dependency failed in ~s", [AppDir]),
{ok, VsnString} =
rebar_utils:sh("git --git-dir=\"" ++ AppDir ++ "/.git\" rev-parse --verify HEAD",
rebar_utils:sh("git --git-dir=\"" ++ rebar_utils:escape_double_quotes(AppDir) ++ "/.git\" rev-parse --verify HEAD",
[{use_stdout, false}, {debug_abort_on_error, AbortMsg}]),
Ref = string:strip(VsnString, both, $\n),
{git, Url, {ref, Ref}}.
@ -32,10 +32,11 @@ needs_update(Dir, {git, Url, {tag, Tag}}) ->
not ((Current1 =:= Tag) andalso compare_url(Dir, Url));
needs_update(Dir, {git, Url, {branch, Branch}}) ->
%% Fetch remote so we can check if the branch has changed
{ok, _} = rebar_utils:sh(?FMT("git fetch origin ~s", [Branch]),
SafeBranch = rebar_utils:escape_chars(Branch),
{ok, _} = rebar_utils:sh(?FMT("git fetch origin ~s", [SafeBranch]),
[{cd, Dir}]),
%% Check for new commits to origin/Branch
{ok, Current} = rebar_utils:sh(?FMT("git log HEAD..origin/~s --oneline", [Branch]),
{ok, Current} = rebar_utils:sh(?FMT("git log HEAD..origin/~s --oneline", [SafeBranch]),
[{cd, Dir}]),
?DEBUG("Checking git branch ~s for updates", [Branch]),
not ((Current =:= []) andalso compare_url(Dir, Url));
@ -93,24 +94,33 @@ download(Dir, {git, Url, ""}, State) ->
download(Dir, {git, Url, {branch, Branch}}, _State) ->
ok = filelib:ensure_dir(Dir),
rebar_utils:sh(?FMT("git clone ~s ~s -b ~s --single-branch",
[Url, filename:basename(Dir), Branch]),
[rebar_utils:escape_chars(Url),
rebar_utils:escape_chars(filename:basename(Dir)),
rebar_utils:escape_chars(Branch)]),
[{cd, filename:dirname(Dir)}]);
download(Dir, {git, Url, {tag, Tag}}, _State) ->
ok = filelib:ensure_dir(Dir),
rebar_utils:sh(?FMT("git clone ~s ~s -b ~s --single-branch",
[Url, filename:basename(Dir), Tag]),
[rebar_utils:escape_chars(Url),
rebar_utils:escape_chars(filename:basename(Dir)),
rebar_utils:escape_chars(Tag)]),
[{cd, filename:dirname(Dir)}]);
download(Dir, {git, Url, {ref, Ref}}, _State) ->
ok = filelib:ensure_dir(Dir),
rebar_utils:sh(?FMT("git clone -n ~s ~s", [Url, filename:basename(Dir)]),
rebar_utils:sh(?FMT("git clone -n ~s ~s",
[rebar_utils:escape_chars(Url),
rebar_utils:escape_chars(filename:basename(Dir))]),
[{cd, filename:dirname(Dir)}]),
rebar_utils:sh(?FMT("git checkout -q ~s", [Ref]), [{cd, Dir}]);
download(Dir, {git, Url, Rev}, _State) ->
?WARN("WARNING: It is recommended to use {branch, Name}, {tag, Tag} or {ref, Ref}, otherwise updating the dep may not work as expected.", []),
ok = filelib:ensure_dir(Dir),
rebar_utils:sh(?FMT("git clone -n ~s ~s", [Url, filename:basename(Dir)]),
rebar_utils:sh(?FMT("git clone -n ~s ~s",
[rebar_utils:escape_chars(Url),
rebar_utils:escape_chars(filename:basename(Dir))]),
[{cd, filename:dirname(Dir)}]),
rebar_utils:sh(?FMT("git checkout -q ~s", [Rev]), [{cd, Dir}]).
rebar_utils:sh(?FMT("git checkout -q ~s", [rebar_utils:escape_chars(Rev)]),
[{cd, Dir}]).
make_vsn(Dir) ->
{Vsn, RawRef, RawCount} = collect_default_refcount(Dir),
@ -162,7 +172,7 @@ get_patch_count(Dir, RawRef) ->
AbortMsg = "Getting rev-list of git dep failed in " ++ Dir,
Ref = re:replace(RawRef, "\\s", "", [global]),
Cmd = io_lib:format("git rev-list ~s..HEAD",
[Ref]),
[rebar_utils:escape_chars(Ref)]),
{ok, PatchLines} = rebar_utils:sh(Cmd,
[{use_stdout, false},
{cd, Dir},

+ 19
- 10
src/rebar_hg_resource.erl View File

@ -57,26 +57,34 @@ download(Dir, {hg, Url, ""}, State) ->
download(Dir, {hg, Url, {branch, Branch}}, _State) ->
ok = filelib:ensure_dir(Dir),
rebar_utils:sh(?FMT("hg clone -q -b ~s ~s ~s",
[Branch, Url, filename:basename(Dir)]),
[rebar_utils:escape_chars(Branch),
rebar_utils:escape_chars(Url),
rebar_utils:escape_chars(filename:basename(Dir))]),
[{cd, filename:dirname(Dir)}]);
download(Dir, {hg, Url, {tag, Tag}}, _State) ->
ok = filelib:ensure_dir(Dir),
rebar_utils:sh(?FMT("hg clone -q -u ~s ~s ~s",
[Tag, Url, filename:basename(Dir)]),
[rebar_utils:escape_chars(Tag),
rebar_utils:escape_chars(Url),
rebar_utils:escape_chars(filename:basename(Dir))]),
[{cd, filename:dirname(Dir)}]);
download(Dir, {hg, Url, {ref, Ref}}, _State) ->
ok = filelib:ensure_dir(Dir),
rebar_utils:sh(?FMT("hg clone -q -r ~s ~s ~s",
[Ref, Url, filename:basename(Dir)]),
[rebar_utils:escape_chars(Ref),
rebar_utils:escape_chars(Url),
rebar_utils:escape_chars(filename:basename(Dir))]),
[{cd, filename:dirname(Dir)}]);
download(Dir, {hg, Url, Rev}, _State) ->
ok = filelib:ensure_dir(Dir),
rebar_utils:sh(?FMT("hg clone -q -r ~s ~s ~s",
[Rev, Url, filename:basename(Dir)]),
[rebar_utils:escape_chars(Rev),
rebar_utils:escape_chars(Url),
rebar_utils:escape_chars(filename:basename(Dir))]),
[{cd, filename:dirname(Dir)}]).
make_vsn(Dir) ->
BaseHg = "hg -R ';" ++ Dir ++ "' ",
BaseHg = "hg -R \";" ++ rebar_utils:escape_double_quotes(Dir) ++ "\" ",
Ref = get_ref(Dir),
Cmd = BaseHg ++ "log --template \"{latesttag}+build.{latesttagdistance}.rev.{node|short}\""
" --rev " ++ Ref,
@ -95,23 +103,23 @@ make_vsn(Dir) ->
%%% Internal functions
compare_url(Dir, Url) ->
CurrentUrl = string:strip(os:cmd("hg -R ';" ++ Dir ++"' paths default"), both, $\n),
CurrentUrl = string:strip(os:cmd("hg -R \";" ++ rebar_utils:escape_double_quotes(Dir) ++"\" paths default"), both, $\n),
CurrentUrl1 = string:strip(CurrentUrl, both, $\r),
parse_hg_url(CurrentUrl1) =:= parse_hg_url(Url).
get_ref(Dir) ->
AbortMsg = io_lib:format("Get ref of hg dependency failed in ~s", [Dir]),
{ok, RefString} =
rebar_utils:sh("hg -R ';" ++ Dir ++ "' --debug id -i",
rebar_utils:sh("hg -R \";" ++ rebar_utils:escape_double_quotes(Dir) ++ "\" --debug id -i",
[{use_stdout, false}, {debug_abort_on_error, AbortMsg}]),
string:strip(RefString, both, $\n).
get_tag_distance(Dir, Ref) ->
AbortMsg = io_lib:format("Get tag distance of hg dependency failed in ~s", [Dir]),
{ok, LogString} =
rebar_utils:sh("hg -R ';" ++ Dir ++ "' "
rebar_utils:sh("hg -R \";" ++ rebar_utils:escape_double_quotes(Dir) ++ "\" "
"log --template \"{latesttag}-{latesttagdistance}\n\" "
"--rev " ++ Ref,
"--rev " ++ rebar_utils:escape_chars(Ref),
[{use_stdout, false}, {debug_abort_on_error, AbortMsg}]),
Log = string:strip(LogString,
both, $\n),
@ -121,7 +129,8 @@ get_tag_distance(Dir, Ref) ->
get_branch_ref(Dir, Branch) ->
AbortMsg = io_lib:format("Get branch ref of hg dependency failed in ~s", [Dir]),
{ok, BranchRefString} =
rebar_utils:sh("hg -R '" ++ Dir ++ "' log --template \"{node}\n\" --rev " ++ Branch,
rebar_utils:sh("hg -R \"" ++ rebar_utils:escape_double_quotes(Dir) ++
"\" log --template \"{node}\n\" --rev " ++ rebar_utils:escape_chars(Branch),
[{use_stdout, false}, {debug_abort_on_error, AbortMsg}]),
string:strip(BranchRefString, both, $\n).

+ 16
- 1
src/rebar_utils.erl View File

@ -57,7 +57,10 @@
tup_umerge/2,
tup_sort/1,
line_count/1,
set_httpc_options/0]).
set_httpc_options/0,
escape_chars/1,
escape_double_quotes/1,
escape_double_quotes_weak/1]).
%% for internal use only
-export([otp_release/0]).
@ -684,3 +687,15 @@ set_httpc_options(_, []) ->
set_httpc_options(Scheme, Proxy) ->
{ok, {_, _, Host, Port, _, _}} = http_uri:parse(Proxy),
httpc:set_options([{Scheme, {{Host, Port}, []}}], rebar).
%% escape\ as\ a\ shell\?
escape_chars(Str) ->
re:replace(Str, "([ ()?`!$])", "\\\\&", [global, {return, list}]).
%% "escape inside these"
escape_double_quotes(Str) ->
re:replace(Str, "([\"\\\\`!$*])", "\\\\&", [global, {return, list}]).
%% "escape inside these" but allow *
escape_double_quotes_weak(Str) ->
re:replace(Str, "([\"\\\\`!$])", "\\\\&", [global, {return, list}]).

Loading…
Cancel
Save