summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTristan Sloughter <t@crashfast.com>2015-05-09 23:01:17 -0500
committerTristan Sloughter <t@crashfast.com>2015-05-12 19:55:06 -0500
commit819d0fb06fbaaec25b1929e971964d5757ba59ff (patch)
tree6e2b184bc344975ac5e3d5f8c93326c647a9e30d
parent7905a9f19cdb0ca2ff70567613784084e3e0a2fd (diff)
verify checksums of hex packages
-rw-r--r--src/rebar_fetch.erl48
-rw-r--r--src/rebar_packages.erl10
-rw-r--r--src/rebar_prv_install_deps.erl16
3 files changed, 55 insertions, 19 deletions
diff --git a/src/rebar_fetch.erl b/src/rebar_fetch.erl
index ec16089..16840eb 100644
--- a/src/rebar_fetch.erl
+++ b/src/rebar_fetch.erl
@@ -40,16 +40,7 @@ download_source(AppDir, Source, State) ->
ok = rebar_file_utils:mv(TmpDir, filename:absname(AppDir1)),
true;
{tarball, File} ->
- ec_file:mkdir_p(AppDir1),
- {ok, Files} = erl_tar:extract(File, [memory]),
-
- code:del_path(filename:absname(filename:join(AppDir1, "ebin"))),
- ec_file:remove(filename:absname(AppDir1), [recursive]),
-
- {"contents.tar.gz", Binary} = lists:keyfind("contents.tar.gz", 1, Files),
- ok = erl_tar:extract({binary, Binary},
- [{cwd, filename:absname(AppDir1)}, compressed]),
- true
+ verify_and_extract(File, Source, AppDir1, State)
end
catch
C:T ->
@@ -69,7 +60,11 @@ needs_update(AppDir, Source, State) ->
end.
format_error({fetch_fail, Source}) ->
- io_lib:format("Failed to fetch and copy dep: ~p", [Source]).
+ io_lib:format("Failed to fetch and copy dep: ~p", [Source]);
+format_error({bad_checksum, File}) ->
+ io_lib:format("Checksum mismatch against tarball in ~s", [File]);
+format_error({bad_registry_checksum, File}) ->
+ io_lib:format("Checksum mismatch against registry in ~s", [File]).
get_resource_type({Type, Location}, Resources) ->
find_resource_module(Type, Location, Resources);
@@ -93,3 +88,34 @@ find_resource_module(Type, Location, Resources) ->
{Type, Module} ->
Module
end.
+
+verify_and_extract(File, Source, AppDir, State) ->
+ ec_file:mkdir_p(AppDir),
+ {ok, Files} = erl_tar:extract(File, [memory]),
+
+ code:del_path(filename:absname(filename:join(AppDir, "ebin"))),
+ ec_file:remove(filename:absname(AppDir), [recursive]),
+
+ {"contents.tar.gz", Contents} = lists:keyfind("contents.tar.gz", 1, Files),
+ {"VERSION", Version} = lists:keyfind("VERSION", 1, Files),
+ {"metadata.config", Meta} = lists:keyfind("metadata.config", 1, Files),
+
+ Checksum = checksum(Contents, Version, Meta),
+ RegistryChecksum = rebar_packages:registry_checksum(Source, State),
+ {"CHECKSUM", TarChecksum} = lists:keyfind("CHECKSUM", 1, Files),
+
+ if
+ Checksum =/= TarChecksum ->
+ ?PRV_ERROR({bad_checksum, File});
+ Checksum =/= RegistryChecksum ->
+ ?PRV_ERROR({bad_registry_checksum, File});
+ true ->
+ ok = erl_tar:extract({binary, Contents},
+ [{cwd, filename:absname(AppDir)}, compressed]),
+ true
+ end.
+
+checksum(Contents, Version, Meta) ->
+ Blob = <<Version/binary, Meta/binary, Contents/binary>>,
+ <<X:256/big-unsigned-integer>> = crypto:hash(sha256, Blob),
+ list_to_binary(string:to_upper(lists:flatten(io_lib:format("~64.16.0b", [X])))).
diff --git a/src/rebar_packages.erl b/src/rebar_packages.erl
index 4ab5f9f..a344328 100644
--- a/src/rebar_packages.erl
+++ b/src/rebar_packages.erl
@@ -3,6 +3,7 @@
-export([get_packages/1
,registry/1
,check_registry/3
+ ,registry_checksum/2
,find_highest_matching/3]).
-export_type([package/0]).
@@ -66,6 +67,15 @@ check_registry(Pkg, Vsn, State) ->
false
end.
+registry_checksum({pkg, Name, Vsn}, State) ->
+ {ok, Registry} = registry(State),
+ case ets:lookup(Registry, {Name, Vsn}) of
+ [{{_, _}, [_, Checksum | _]}] ->
+ Checksum;
+ [] ->
+ none
+ end.
+
%% Hex supports use of ~> to specify the version required for a dependency.
%% Since rebar3 requires exact versions to choose from we find the highest
%% available version of the dep that passes the constraint.
diff --git a/src/rebar_prv_install_deps.erl b/src/rebar_prv_install_deps.erl
index bc0a113..2570bc0 100644
--- a/src/rebar_prv_install_deps.erl
+++ b/src/rebar_prv_install_deps.erl
@@ -563,10 +563,10 @@ fetch_app(AppInfo, AppDir, State) ->
?INFO("Fetching ~s (~p)", [rebar_app_info:name(AppInfo), rebar_app_info:source(AppInfo)]),
Source = rebar_app_info:source(AppInfo),
case rebar_fetch:download_source(AppDir, Source, State) of
- {error, Reason} ->
- throw(Reason);
- Result ->
- Result
+ true ->
+ true;
+ Error ->
+ throw(Error)
end.
update_app_info(AppInfo) ->
@@ -587,10 +587,10 @@ maybe_upgrade(AppInfo, AppDir, true, State) ->
true ->
?INFO("Upgrading ~s", [rebar_app_info:name(AppInfo)]),
case rebar_fetch:download_source(AppDir, Source, State) of
- {error, Reason} ->
- throw(Reason);
- Result ->
- Result
+ true ->
+ true;
+ Error ->
+ throw(Error)
end;
false ->
?INFO("No upgrade needed for ~s", [rebar_app_info:name(AppInfo)]),