From b64d6a4c56518a3e17a3a4842271f12edde24b94 Mon Sep 17 00:00:00 2001 From: Tyler Witt Date: Mon, 27 Jan 2025 06:03:49 +0900 Subject: [PATCH] Hex: Encode exceptions (#11406) * Capture exceptions raised from Mix Mix.Dep.Converger.converge can raise exceptions, which then leak the exception into IO as non-encoded binary. Basically, since we now encode/decode base64, anything non base64 is going to fail. * Unskip test Ran rspec locally 45 times and it never failed. * fixup --- hex/helpers/lib/parse_deps.exs | 33 +++++++++++----- hex/spec/dependabot/hex/file_parser_spec.rb | 43 ++++++++++++--------- 2 files changed, 48 insertions(+), 28 deletions(-) diff --git a/hex/helpers/lib/parse_deps.exs b/hex/helpers/lib/parse_deps.exs index 950cce1685..f4b2240dd5 100644 --- a/hex/helpers/lib/parse_deps.exs +++ b/hex/helpers/lib/parse_deps.exs @@ -1,11 +1,25 @@ defmodule Parser do + @allowed_scms [Hex.SCM, Mix.SCM.Git, Mix.SCM.Path] + def run do # This is necessary because we can't specify :extra_applications to have :hex in other mixfiles. Mix.ensure_application!(:hex) - Mix.Dep.Converger.converge() - |> Enum.flat_map(&parse_dep/1) - |> Enum.map(&build_dependency(&1.opts[:lock], &1)) + with {:ok, deps} <- converge_deps() do + result = + for %Mix.Dep{scm: scm} = dep <- deps, scm in @allowed_scms, + expanded_dep <- expand_deps(dep) do + build_dependency(expanded_dep.opts[:lock], expanded_dep) + end + + {:ok, result} + end + end + + defp converge_deps do + {:ok, Mix.Dep.Converger.converge()} + rescue e -> + {:error, Exception.format_banner(:error, e, __STACKTRACE__)} end defp build_dependency(nil, dep) do @@ -39,7 +53,7 @@ defmodule Parser do defp parse_groups(only), do: [only] # path dependency - defp parse_dep(%{scm: Mix.SCM.Path, opts: opts} = dep) do + defp expand_deps(%{scm: Mix.SCM.Path, opts: opts} = dep) do cond do # umbrella dependency - ignore opts[:in_umbrella] -> @@ -55,10 +69,7 @@ defmodule Parser do end # hex, git dependency - defp parse_dep(%{scm: scm} = dep) when scm in [Hex.SCM, Mix.SCM.Git], do: [dep] - - # unsupported - defp parse_dep(_dep), do: [] + defp expand_deps(%{scm: scm} = dep) when scm in [Hex.SCM, Mix.SCM.Git], do: [dep] defp umbrella_top_level_dep?(dep) do if Mix.Project.umbrella?() do @@ -85,7 +96,9 @@ defmodule Parser do |> empty_str_to_nil() end - defp maybe_regex_to_str(s), do: if(Regex.regex?(s), do: Regex.source(s), else: s) + defp maybe_regex_to_str(%Regex{} = s), do: Regex.source(s) + defp maybe_regex_to_str(s), do: s + defp empty_str_to_nil(""), do: nil defp empty_str_to_nil(s), do: s @@ -102,7 +115,7 @@ defmodule Parser do end end -{:ok, Parser.run()} +Parser.run() |> :erlang.term_to_binary() |> Base.encode64() |> IO.write() diff --git a/hex/spec/dependabot/hex/file_parser_spec.rb b/hex/spec/dependabot/hex/file_parser_spec.rb index 538bffb4bc..de640d6175 100644 --- a/hex/spec/dependabot/hex/file_parser_spec.rb +++ b/hex/spec/dependabot/hex/file_parser_spec.rb @@ -376,7 +376,7 @@ ) end - it "parses the dependencies correctly", skip: "Hex upgrade work is in progress" do + it "parses the dependencies correctly" do expect(dependencies.length).to eq(3) expect(dependencies).to include( Dependabot::Dependency.new( @@ -391,24 +391,31 @@ package_manager: "hex" ) ) - expect(dependencies).to include( - Dependabot::Dependency.new( - name: "plug", - version: "1.3.6", - requirements: [{ - requirement: "~> 1.3.0", - file: "apps/dependabot_business/mix.exs", - groups: [], - source: nil - }, { - requirement: "1.3.6", - file: "apps/dependabot_web/mix.exs", - groups: [], - source: nil - }], - package_manager: "hex" - ) + + plug_expectation = Dependabot::Dependency.new( + name: "plug", + version: "1.3.6", + requirements: [{ + requirement: "~> 1.3.0", + file: "apps/dependabot_business/mix.exs", + groups: [], + source: nil + }, { + requirement: "1.3.6", + file: "apps/dependabot_web/mix.exs", + groups: [], + source: nil + }], + package_manager: "hex" ) + + plug_dep = dependencies.find { |d| d.name == "plug" } + + expect(plug_dep.name).to eq(plug_expectation.name) + expect(plug_dep.version).to eq(plug_expectation.version) + expect(plug_dep.requirements).to match_array(plug_expectation.requirements) + expect(plug_dep.package_manager).to eq(plug_expectation.package_manager) + expect(dependencies).to include( Dependabot::Dependency.new( name: "distillery",