diff --git a/mix.exs b/mix.exs index 5a1632d..b321cb3 100644 --- a/mix.exs +++ b/mix.exs @@ -55,7 +55,7 @@ defmodule MaveCore.MixProject do {:ecto_sql, "~> 3.14.0"}, {:postgrex, "~> 0.22.3"}, {:decimal, "~> 3.1", override: true}, - {:phoenix_html, "~> 4.1"}, + {:phoenix_html, "~> 4.4"}, {:phoenix_live_reload, "~> 1.2", only: :dev}, {:phoenix_live_view, "~> 1.2.13"}, {:lazy_html, ">= 0.1.13", only: :test}, @@ -96,6 +96,8 @@ defmodule MaveCore.MixProject do {:cors_plug, "~> 3.0"}, {:phoenix_storybook, "~> 1.3", only: [:dev, :test]}, {:hackney, "~> 4.9.1", override: true}, + # Enforce the hostname verification fix for downstream dependency resolution. + {:ssl_verify_fun, "~> 1.1.9"}, {:credo, "~> 1.7", only: [:dev, :test], runtime: false}, {:mix_audit, "~> 2.1", only: [:dev, :test], runtime: false}, {:sobelow, "~> 0.14", only: [:dev, :test], runtime: false}, diff --git a/mix.lock b/mix.lock index 1d2a3e9..f638b57 100644 --- a/mix.lock +++ b/mix.lock @@ -60,7 +60,7 @@ "partitioned_buffer": {:hex, :partitioned_buffer, "0.4.2", "2236c090dd32916a59a8e22a7251b416d679addefa4e79b78777a549406f17ac", [:mix], [{:nimble_options, "~> 1.0", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:telemetry, "~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "64019407fd6e0822d591ac7060c1180e3f746691bbc5ace29c7a0100e469ef6e"}, "phoenix": {:hex, :phoenix, "1.8.16", "6a4742054740496bfabefc95253f978d8f595f07bb601039b7a18b384a873916", [:mix], [{:bandit, "~> 1.0", [hex: :bandit, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 2.4", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: true]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.7", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:plug_crypto, "~> 2.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}, {:websock_adapter, "~> 0.5", [hex: :websock_adapter, repo: "hexpm", optional: false]}], "hexpm", "e7355c35889305bc539eb09dae2393abcbaf0f65f6874a3f0dcb5f0ec53737fe"}, "phoenix_ecto": {:hex, :phoenix_ecto, "4.7.0", "75c4b9dfb3efdc42aec2bd5f8bccd978aca0651dbcbc7a3f362ea5d9d43153c6", [:mix], [{:ecto, "~> 3.5", [hex: :ecto, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 2.14.2 or ~> 3.0 or ~> 4.1", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:plug, "~> 1.9", [hex: :plug, repo: "hexpm", optional: false]}, {:postgrex, "~> 0.16 or ~> 1.0", [hex: :postgrex, repo: "hexpm", optional: true]}], "hexpm", "1d75011e4254cb4ddf823e81823a9629559a1be93b4321a6a5f11a5306fbf4cc"}, - "phoenix_html": {:hex, :phoenix_html, "4.3.0", "d3577a5df4b6954cd7890c84d955c470b5310bb49647f0a114a6eeecc850f7ad", [:mix], [], "hexpm", "3eaa290a78bab0f075f791a46a981bbe769d94bc776869f4f3063a14f30497ad"}, + "phoenix_html": {:hex, :phoenix_html, "4.4.0", "d4007bc52fee85af48c30a0d541a5b0590e4f1f12f2f9c227bb004620090c0e3", [:mix], [], "hexpm", "94832038da7ab8f86d4c0d36f7797a269fd06fe611f25c69f55108c075505228"}, "phoenix_live_dashboard": {:hex, :phoenix_live_dashboard, "0.8.7", "405880012cb4b706f26dd1c6349125bfc903fb9e44d1ea668adaf4e04d4884b7", [:mix], [{:ecto, "~> 3.6.2 or ~> 3.7", [hex: :ecto, repo: "hexpm", optional: true]}, {:ecto_mysql_extras, "~> 0.5", [hex: :ecto_mysql_extras, repo: "hexpm", optional: true]}, {:ecto_psql_extras, "~> 0.7", [hex: :ecto_psql_extras, repo: "hexpm", optional: true]}, {:ecto_sqlite3_extras, "~> 1.1.7 or ~> 1.2.0", [hex: :ecto_sqlite3_extras, repo: "hexpm", optional: true]}, {:mime, "~> 1.6 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:phoenix_live_view, "~> 0.19 or ~> 1.0", [hex: :phoenix_live_view, repo: "hexpm", optional: false]}, {:telemetry_metrics, "~> 0.6 or ~> 1.0", [hex: :telemetry_metrics, repo: "hexpm", optional: false]}], "hexpm", "3a8625cab39ec261d48a13b7468dc619c0ede099601b084e343968309bd4d7d7"}, "phoenix_live_reload": {:hex, :phoenix_live_reload, "1.6.2", "b18b0773a1ba77f28c52decbb0f10fd1ac4d3ae5b8632399bbf6986e3b665f62", [:mix], [{:file_system, "~> 0.2.10 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.4", [hex: :phoenix, repo: "hexpm", optional: false]}], "hexpm", "d1f89c18114c50d394721365ffb428cce24f1c13de0467ffa773e2ff4a30d5b9"}, "phoenix_live_view": {:hex, :phoenix_live_view, "1.2.13", "850284c529f013e82d9596325bd92cb5859c0872176616b6e2ec4294e2ef616e", [:mix], [{:igniter, ">= 0.6.16 and < 1.0.0-0", [hex: :igniter, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:lazy_html, "~> 0.1.0", [hex: :lazy_html, repo: "hexpm", optional: true]}, {:phoenix, "~> 1.6.15 or ~> 1.7.0 or ~> 1.8.0", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 3.3 or ~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: true]}, {:plug, "~> 1.15", [hex: :plug, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.2 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "a5cd2a3df10552bce6b7e68e5d376ff3b46cca3ca62d6c43bd4398d80c197fbe"}, @@ -76,7 +76,7 @@ "req_s3": {:hex, :req_s3, "0.2.5", "8570619fcf8c820f4f2b3c551695b35f3e4d9c11ca3f8e638aa5a69d65e797c7", [:mix], [{:req, "~> 0.5.6 or ~> 0.6.0 or ~> 0.7.0", [hex: :req, repo: "hexpm", optional: false]}], "hexpm", "f675afe738b7d323a0a6d94e984cb8a7d1acc2f26fb6160d001eeaed4873ed44"}, "rustler_precompiled": {:hex, :rustler_precompiled, "0.9.0", "3a052eda09f3d2436364645cc1f13279cf95db310eb0c17b0d8f25484b233aa0", [:mix], [{:rustler, "~> 0.23", [hex: :rustler, repo: "hexpm", optional: true]}], "hexpm", "471d97315bd3bf7b64623418b3693eedd8e47de3d1cb79a0ac8f9da7d770d94c"}, "sobelow": {:hex, :sobelow, "0.14.1", "2f81e8632f15574cba2402bcddff5497b413c01e6f094bc0ab94e83c2f74db81", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "8fac9a2bd90fdc4b15d6fca6e1608efb7f7c600fa75800813b794ee9364c87f2"}, - "ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.7", "354c321cf377240c7b8716899e182ce4890c5938111a1296add3ec74cf1715df", [:make, :mix, :rebar3], [], "hexpm", "fe4c190e8f37401d30167c8c405eda19469f34577987c76dde613e838bbc67f8"}, + "ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.9", "4ff292dc16e728fde1235cf5fa6bcd3ca01864004526f71a4a70935509d1a7f1", [:make, :mix, :rebar3], [], "hexpm", "19f24ad715ef6255d530b2f0db8b2d2191ae435b6527234e943f523e84ed0895"}, "swoosh": {:hex, :swoosh, "1.27.0", "2df57c342f3854f2d429e6afebf19a03a179976a64006a93750ed81639453f75", [:mix], [{:bandit, ">= 1.0.0", [hex: :bandit, repo: "hexpm", optional: true]}, {:cowboy, "~> 1.1 or ~> 2.4", [hex: :cowboy, repo: "hexpm", optional: true]}, {:ex_aws, "~> 2.1", [hex: :ex_aws, repo: "hexpm", optional: true]}, {:finch, "~> 0.6", [hex: :finch, repo: "hexpm", optional: true]}, {:gen_smtp, "~> 0.13 or ~> 1.0", [hex: :gen_smtp, repo: "hexpm", optional: true]}, {:hackney, ">= 1.9.0 and < 5.0.0", [hex: :hackney, repo: "hexpm", optional: true]}, {:idna, ">= 6.0.0 and < 8.0.0", [hex: :idna, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mail, "~> 0.2", [hex: :mail, repo: "hexpm", optional: true]}, {:mime, "~> 1.1 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mua, "~> 0.2.3", [hex: :mua, repo: "hexpm", optional: true]}, {:multipart, "~> 0.4", [hex: :multipart, repo: "hexpm", optional: true]}, {:plug, "~> 1.9", [hex: :plug, repo: "hexpm", optional: true]}, {:plug_cowboy, ">= 1.0.0", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:req, "~> 0.5.10 or ~> 0.6 or ~> 1.0", [hex: :req, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.2 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "5da7d3b11de5d61327275ed599cb311942ea6e23cdbb411981f46ee150cddf76"}, "tailwind": {:hex, :tailwind, "0.5.2", "d8bc230361fd3e14b495043c364aefddfac2ad4bf2eada380f458e9a15cb2e20", [:mix], [], "hexpm", "e0e5fd9799fb9231b97e52ce074d9ff509043d1946713a71fbc608e81eff7e66"}, "telemetry": {:hex, :telemetry, "1.4.2", "a0cb522801dffb1c49fe6e30561badffc7b6d0e180db1300df759faa22062855", [:rebar3], [], "hexpm", "928f6495066506077862c0d1646609eed891a4326bee3126ba54b60af61febb1"}, diff --git a/test/mave_core/dependency_security_test.exs b/test/mave_core/dependency_security_test.exs new file mode 100644 index 0000000..c0d09f8 --- /dev/null +++ b/test/mave_core/dependency_security_test.exs @@ -0,0 +1,77 @@ +defmodule MaveCore.DependencySecurityTest do + use ExUnit.Case, async: true + + require Record + + Record.defrecordp( + :certificate, + :OTPCertificate, + Record.extract(:OTPCertificate, from_lib: "public_key/include/public_key.hrl") + ) + + Record.defrecordp( + :certificate_body, + :OTPTBSCertificate, + Record.extract(:OTPTBSCertificate, from_lib: "public_key/include/public_key.hrl") + ) + + Record.defrecordp( + :extension, + :Extension, + Record.extract(:Extension, from_lib: "public_key/include/public_key.hrl") + ) + + test "partial certificate wildcards cannot consume another DNS label" do + for {pattern, hostname} <- [ + {~c"*o.example.com", ~c"bar.foo.example.com"}, + {~c"f*o.example.com", ~c"foo.foo.example.com"} + ] do + assert {:fail, :unable_to_match_altnames} = + :ssl_verify_hostname.verify_cert_hostname(dns_certificate(pattern), hostname) + end + end + + test "ordinary certificate names and single-label wildcards still match" do + for {pattern, hostname} <- [ + {~c"www.example.com", ~c"www.example.com"}, + {~c"*.example.com", ~c"www.example.com"}, + {~c"f*o.example.com", ~c"foo.example.com"} + ] do + assert {:valid, ^hostname} = + :ssl_verify_hostname.verify_cert_hostname(dns_certificate(pattern), hostname) + end + end + + test "untrusted HTML attribute names cannot introduce additional attributes" do + for attrs <- [ + [{"x onmouseover", "alert(1)"}], + [{"x\tonmouseover", "alert(1)"}], + [{"x onclick=alert(1)", true}], + [data: [{"x onmouseover", "alert(1)"}]] + ] do + assert_raise ArgumentError, fn -> Phoenix.HTML.attributes_escape(attrs) end + end + end + + test "legitimate HTML attributes retain nesting and value escaping" do + html = + [title: "<>&\"", data: [item_id: "one"], phx_value_id: "two"] + |> Phoenix.HTML.attributes_escape() + |> Phoenix.HTML.safe_to_string() + + assert html =~ "title=\"<>&"\"" + assert html =~ "data-item-id=\"one\"" + assert html =~ "phx-value-id=\"two\"" + end + + defp dns_certificate(pattern) do + certificate( + tbsCertificate: + certificate_body( + extensions: [ + extension(extnID: {2, 5, 29, 17}, critical: false, extnValue: [dNSName: pattern]) + ] + ) + ) + end +end