Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 6 additions & 5 deletions lib/mix/lib/mix/local.ex
Original file line number Diff line number Diff line change
Expand Up @@ -76,11 +76,11 @@ defmodule Mix.Local do

@doc """
Fetches the given signed CSV files, verify and return the matching
Elixir version with checksum.
Elixir version, artifact version and artifact's checksum.

Used to install both Rebar and Hex from S3.
"""
def find_matching_elixir_version_from_signed_csv!(name, path) do
def find_matching_versions_from_signed_csv!(name, path) do
csv = read_path!(name, path)

signature =
Expand All @@ -96,7 +96,8 @@ defmodule Mix.Local do
Mix.raise "Could not install #{name} because Mix could not verify authenticity " <>
"of metadata file at #{path}. This may happen because a proxy or some " <>
"entity is interfering with the download or because you don't have a " <>
"public key to verify the download"
"public key to verify the download. Add the corresponding public key " <>
"or try again later"
end
end

Expand Down Expand Up @@ -125,9 +126,9 @@ defmodule Mix.Local do
|> Enum.find_value(entries, &find_version(&1, current_version))
end

defp find_version([_, digest|versions], current_version) do
defp find_version([artifact_version, digest|versions], current_version) do
if version = Enum.find(versions, &Version.compare(&1, current_version) != :gt) do
{version, digest}
{version, artifact_version, digest}
end
end
end
12 changes: 9 additions & 3 deletions lib/mix/lib/mix/tasks/local.hex.ex
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ defmodule Mix.Tasks.Local.Hex do

@hex_s3 "https://s3.amazonaws.com/s3.hex.pm"
@hex_list_url @hex_s3 <> "/installs/hex-1.x.csv"
@hex_archive_url @hex_s3 <> "/installs/[VERSION]/hex.ez"
@hex_archive_url @hex_s3 <> "/installs/[ELIXIR_VERSION]/hex-[HEX_VERSION].ez"

@shortdoc "Installs Hex locally"

Expand All @@ -19,8 +19,14 @@ defmodule Mix.Tasks.Local.Hex do
"""
@spec run(OptionParser.argv) :: boolean
def run(args) do
{version, sha512} = Mix.Local.find_matching_elixir_version_from_signed_csv!("Hex", @hex_list_url)
url = String.replace(@hex_archive_url, "[VERSION]", version)
{elixir_version, hex_version, sha512} =
Mix.Local.find_matching_versions_from_signed_csv!("Hex", @hex_list_url)

url =
@hex_archive_url
|> String.replace("[ELIXIR_VERSION]", elixir_version)
|> String.replace("[HEX_VERSION]", hex_version)

Mix.Tasks.Archive.Install.run [url, "--sha512", sha512 | args]
end
end
12 changes: 9 additions & 3 deletions lib/mix/lib/mix/tasks/local.rebar.ex
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ defmodule Mix.Tasks.Local.Rebar do

@rebar_s3 "https://s3.amazonaws.com/s3.hex.pm"
@rebar_list_url @rebar_s3 <> "/installs/rebar-1.x.csv"
@rebar_escript_url @rebar_s3 <> "/installs/[VERSION]/rebar"
@rebar_escript_url @rebar_s3 <> "/installs/[ELIXIR_VERSION]/rebar-[REBAR_VERSION]"

@shortdoc "Installs rebar locally"

Expand Down Expand Up @@ -68,8 +68,14 @@ defmodule Mix.Tasks.Local.Rebar do
end

defp install_from_s3(opts) do
{version, sha512} = Mix.Local.find_matching_elixir_version_from_signed_csv!("Rebar", @rebar_list_url)
url = String.replace(@rebar_escript_url, "[VERSION]", version)
{elixir_version, rebar_version, sha512} =
Mix.Local.find_matching_versions_from_signed_csv!("Rebar", @rebar_list_url)

url =
@rebar_escript_url
|> String.replace("[ELIXIR_VERSION]", elixir_version)
|> String.replace("[REBAR_VERSION]", rebar_version)

install_from_path(url, Keyword.put(opts, :sha512, sha512))
end
end
97 changes: 97 additions & 0 deletions lib/mix/test/mix/local_test.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
Code.require_file "../test_helper.exs", __DIR__

defmodule Mix.LocalTest do
use MixTest.Case, async: true

# openssl rsa -in elixirest.pem -pubout > elixirest.pub
@public_key """
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA37moKP1dGGLhsP3d8Fwv
W25SoYZUY2K+Iq7A0OBV36Rnb8yW3BWjfh5YtmPvUCfYUbNCW2HTMMgBntkQ4YmN
B9tHVZazl2uX9lGCfZZPFc/9umvKRojCPkMN81MfTxqnY0oaLHr6DB86RsWHB+ld
782Xf+nd9q3LFdUl8SGlKX7uzfVWd4EWYNcL7aLeLSupZWeNg8uVmY3zua0EgIlQ
XryalIOZb/R+pwprWZoftCl+20FGYi/mJpo/idFtXsR0sJKF4X0W3NORT9RIRbs9
WdjiFi+eIP7Nm8KSF4pbaXCqSmVf9cgvUuGTxc9/P5GcIPAlkcsSrE5peLyUCk5f
2QIDAQAB
-----END PUBLIC KEY-----
"""

# openssl genrsa -aes256 -out elixirtest.pem -passout stdin 2048
@private_key """
-----BEGIN RSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: AES-256-CBC,48BA5153DA2F120ECE063B33C1204A49

5gp3daNWujH7o9S/dJQEt9TYTRP0pPZtU55PlZrzWt52optr7XHW/ENOm84g5J70
QCPELp12jfQsNiPwbVWXKy2zD3QlNiAelf65hqLWJTWli7XIXfdP46VXOu67OKf9
Ziw4HQ+AdBEwFt20wJst77iy17sNlyxp5DhNDonnSizzIowgUAJkoNI5aBUU6D8X
KTSIftZW35Z4SudkazdoHepEfItZTI8mB4rvfn71Q4oOBA1rAuUUmdPWoPBfUHDa
hvIp2T2Q8zZYqm0+SjDxZUYOOreE7fuf5NSLhHHt7+jyWQmtaVxnOWms72G+9xT0
NGmOEB0WEg1kBsUbYOXXwyCAZhNA6MaKCtgjQczRTK+geS1xNaFc9FDEk3ZjN4Z8
PxrKQoqo+2aQGVcatZWCom80Dci3bIv7iZNA/y1rjfBn+MeitMOGscP7/CBrJAbI
bh1mvCu0McSnqlN0a+EuCVfJQYFMzjibpRVzKAST0QeaxXd5QxHfcPFPBLOpiVWc
NjHaZsHORyoJbUKGA4rgOiSB63mv7SDRA2mvxWpwV/+6MuwBah6t6CGoEsAr1Hbn
1ySt5w27bw3QEf2KTiuxDubo8UrF0eYzP5A9MH8vRpSRZHg8T3SBVfPJ/pM16Lnn
5BaMUdxDFJeet5HUYoke9Zm3udh2BvwGiKhzc9Pbw/EcsCcvChMimRTasqTaRf+S
uIm0Un7o+7kTuvBo2y87j2urCEUzft5QqEynbkR7p3vZnwoLLj+supXh3V8ivW4s
Z6ql+ukRcWd/ode+lbSiYfAJCLc1tCqJ3kTnMnADJBlL0TX7YnwBwWuwwPuZgeAv
F6nnBE1SBQ1WK+bjSVzIqmNFqsZw34wgpnz2heX0q8msF5pzd6EIeA+uz86k8XYh
4eVZYGXxa4Exodh/MqEpRuN1ytWDXvHULh0gml7xwZC3R50UD8uBNt5RGjXUkjXc
V0atKuvgzVlsB4xbDhVP7EVYHBF02NfNOsvo7kh0Yl1IcT/42UaCGYuU1o9zotPv
9b3SHz/HOmBVj2uCdR5XZ4EolP5Iv9vqIDt9DsuDpOyO+AFOww0FnJNCQ1Hmfb0T
qBYPv994oSPYLCGR4a8i/xfmmV8KbAIVEgK3AMbz8RxKr3WBWXWnzQdr4+y4EG24
hSnR52XQ42edv/fkqf9ez+fKNQ9i7PtlPE96Q21NeLMNKHh43X8hJFDh+oPz3Aio
YSNMCZnoyRdrjBRCsVBpnyoLmuhWwG9RlcrEj3G0BxYPh/weaBOAKAHjSr28yuUj
yIa8uddszC6XHSiVUgu7SGO8gQmq++eNdckjX/pEug5MjcWLUqaUg6+YLFWY6NLf
uDPOYuivq7ErtKTvP2xl3TBEDKhdfqxA2+RFxbBDmKjffZnRkcknQsxhlzAdbg22
Jwa2B1nrfjJpX5F+1Av2jHQGbIKMqZzv8fo1binMKpptFzokbWEOjcPCb3tPuomG
ZRkW3qO2pdyYX2N7VXYG9tGi2HrN/oFrWnHPoYF23v85V8WxNkODOCpTz85e6R5v
PVu+FCNFj5weEOTRhtEQyJo7mU5qIRwYeZvVxiC6W+XeFs95wdBE/Lvpg8yZ8D9d
-----END RSA PRIVATE KEY-----
"""

@csv """
1.2.5,ABC,0.9.0
1.2.3,DEF,1.0.0
1.2.4,GHI,1.0.0
"""

# openssl dgst -sha512 -sign elixirtest.pem hex-1.x.csv | openssl base64 > elixirtest.csv.signed
@csv_signed """
VRydmXOdEXQcKJu/SK/nKnE00T+s/T4mpXrYROMSXhD/s8ClvdimnGg61ie3YBS6
LXOjlEhbtMHRM2rTOUvv4z7FcyzwvSxSjunlVi2g3c1pVOZ78MonnYhGb44tZw/q
SOVmV+jJhc9EZFMIAAM3plMoyssyw2pMh7ZB/DxCQTIem3Qf0Ujzc2bYkLVlw7R+
1Rn6dcYEgCzyldVkAUMaYBwieyweWALA+YVDCMudJJK2J7p1OnuoPSVV+N3OkB/Z
T6Jj5ljD+54XnuxAMcgCoF9lpOwXscnw/Ma+8JqIoWo0jNFE3ji+8dGCUzQUdSe8
llLXgJJE2tGpDhEXBA3idg==
"""

setup_all do
File.mkdir_p!(Mix.PublicKey.public_keys_path)

Path.join(Mix.PublicKey.public_keys_path, "test_key.pub")
|> File.write!(@public_key)
end

test "select correct versions from csv" do
in_tmp "select correct versions from csv", fn ->
File.write!("csv", @csv)
File.write!("csv.signed", @csv_signed)

assert {"1.0.0", "1.2.4", "GHI"} =
Mix.Local.find_matching_versions_from_signed_csv!("name", "csv")
end
end

test "raise on bad signature" do
in_tmp "raise on bad signature", fn ->
csv_signed = String.replace(@csv_signed, "VRy", "BAD")
File.write!("csv", @csv)
File.write!("csv.signed", csv_signed)

assert_raise Mix.Error, fn ->
Mix.Local.find_matching_versions_from_signed_csv!("name", "csv")
end
end
end
end