diff --git a/lib/hex/package.ex b/lib/hex/package.ex index 6565458e..67b1e5e8 100644 --- a/lib/hex/package.ex +++ b/lib/hex/package.ex @@ -53,6 +53,12 @@ defmodule Hex.Package do package. If a `rebar` or `rebar.config` file is present Hex will mark it as able to build with rebar. This detection can be overridden by setting this field. + * `:secret_scan` - Configuration for the repository's secret scanning of + the package. Supports `:ignore`, a list of file path globs whose + findings are suppressed, for paths that deliberately ship secret-like + values such as test fixtures: + + secret_scan: [ignore: ["test/fixtures/**"]] """ end end diff --git a/lib/mix/tasks/hex.build.ex b/lib/mix/tasks/hex.build.ex index 453e93d8..d1fd3fe1 100644 --- a/lib/mix/tasks/hex.build.ex +++ b/lib/mix/tasks/hex.build.ex @@ -33,7 +33,7 @@ defmodule Mix.Tasks.Hex.Build do @error_fields ~w(app name files version build_tools)a @warn_fields ~w(description licenses links)a - @meta_fields @error_fields ++ @warn_fields ++ ~w(elixir extra)a + @meta_fields @error_fields ++ @warn_fields ++ ~w(elixir extra secret_scan)a @root_fields ~w(app version elixir description)a @max_description_length 300 @default_repo "hexpm" @@ -367,8 +367,16 @@ defmodule Mix.Tasks.Hex.Build do end end + defp format_metadata_value([]) do + "" + end + defp format_metadata_value(list) when is_list(list) do - Enum.join(list, ", ") + if Keyword.keyword?(list) do + format_metadata_value(Map.new(list)) + else + Enum.join(list, ", ") + end end defp format_metadata_value(map) when is_map(map) do diff --git a/test/mix/tasks/hex.build_test.exs b/test/mix/tasks/hex.build_test.exs index 2411263c..b4ba52c7 100644 --- a/test/mix/tasks/hex.build_test.exs +++ b/test/mix/tasks/hex.build_test.exs @@ -403,6 +403,28 @@ defmodule Mix.Tasks.Hex.BuildTest do purge([ReleaseMeta.MixProject]) end + test "create with secret_scan" do + Process.put(:hex_test_app_name, :build_secret_scan) + Mix.Project.push(ReleaseSecretScan.MixProject) + + in_tmp(fn -> + Hex.State.put(:cache_home, tmp_path()) + + File.write!("myfile.txt", "hello") + File.chmod!("myfile.txt", 0o100644) + + Mix.Tasks.Hex.Build.run(["--unpack"]) + + # A keyword list has to print like a map does, not blow up on the tuples. + assert_received {:mix_shell, :info, [" Secret scan: \n ignore: test/fixtures/**"]} + + {:ok, metadata} = :file.consult("build_secret_scan-0.0.1/hex_metadata.config") + assert {"secret_scan", [{"ignore", ["test/fixtures/**"]}]} in metadata + end) + after + purge([ReleaseSecretScan.MixProject]) + end + test "reject package if description is missing" do Process.put(:hex_test_app_name, :build_no_description) Mix.Project.push(ReleaseNoDescription.MixProject) diff --git a/test/support/release_samples.ex b/test/support/release_samples.ex index 880554a4..42d2a6e9 100644 --- a/test/support/release_samples.ex +++ b/test/support/release_samples.ex @@ -29,6 +29,22 @@ defmodule ReleaseSimple.MixProject do end end +defmodule ReleaseSecretScan.MixProject do + def project do + [ + app: Process.get(:hex_test_app_name) || raise("missing app name"), + description: "baz", + version: "0.0.1", + package: [ + licenses: ["MIT"], + files: ["myfile.txt"], + links: %{"a" => "http://a"}, + secret_scan: [ignore: ["test/fixtures/**"]] + ] + ] + end +end + defmodule ReleaseNewSimple.MixProject do def project do [