From 63502b25471f088138850292a6c66862f19a03bc Mon Sep 17 00:00:00 2001 From: Yordis Prieto Date: Thu, 1 Aug 2024 11:06:17 -0400 Subject: [PATCH] feat: add ssl opt to httpc by default (#626) --- lib/tesla/adapter/httpc.ex | 27 ++++++++++++++++++++++++++ mix.exs | 2 +- test/tesla/adapter/httpc_test.exs | 32 +++++++++++++++++++++++++++++++ 3 files changed, 60 insertions(+), 1 deletion(-) diff --git a/lib/tesla/adapter/httpc.ex b/lib/tesla/adapter/httpc.ex index aa6f9550..10b365e7 100644 --- a/lib/tesla/adapter/httpc.ex +++ b/lib/tesla/adapter/httpc.ex @@ -8,6 +8,8 @@ defmodule Tesla.Adapter.Httpc do consistency between adapters """ + current_otp_version = List.to_integer(:erlang.system_info(:otp_release)) + @behaviour Tesla.Adapter import Tesla.Adapter.Shared, only: [stream_to_fun: 1, next_chunk: 1] alias Tesla.Multipart @@ -18,12 +20,37 @@ defmodule Tesla.Adapter.Httpc do @impl Tesla.Adapter def call(env, opts) do opts = Tesla.Adapter.opts(@override_defaults, env, opts) + opts = add_default_ssl_opt(env, opts) with {:ok, {status, headers, body}} <- request(env, opts) do {:ok, format_response(env, status, headers, body)} end end + # TODO: remove this once OTP 25+ is required + if current_otp_version >= 25 do + def add_default_ssl_opt(env, opts) do + default_ssl_opt = [ + ssl: [ + verify: :verify_peer, + cacerts: :public_key.cacerts_get(), + depth: 3, + customize_hostname_check: [ + match_fun: :public_key.pkix_verify_hostname_match_fun(:https) + ], + crl_check: true, + crl_cache: {:ssl_crl_cache, {:internal, [http: 1000]}} + ] + ] + + Tesla.Adapter.opts(default_ssl_opt, env, opts) + end + else + def add_default_ssl_opt(_env, opts) do + opts + end + end + defp format_response(env, {_, status, _}, headers, body) do %{env | status: status, headers: format_headers(headers), body: format_body(body)} end diff --git a/mix.exs b/mix.exs index 9acf4a0d..47fdeca8 100644 --- a/mix.exs +++ b/mix.exs @@ -17,7 +17,7 @@ defmodule Tesla.Mixfile do test_coverage: [tool: ExCoveralls], dialyzer: [ plt_core_path: "_build/#{Mix.env()}", - plt_add_apps: [:mix, :inets, :idna, :ssl_verify_fun, :ex_unit], + plt_add_apps: [:public_key, :mix, :inets, :idna, :ssl_verify_fun, :ex_unit], plt_add_deps: :apps_direct ], docs: docs(), diff --git a/test/tesla/adapter/httpc_test.exs b/test/tesla/adapter/httpc_test.exs index 4e3a7aa8..a498288f 100644 --- a/test/tesla/adapter/httpc_test.exs +++ b/test/tesla/adapter/httpc_test.exs @@ -45,4 +45,36 @@ defmodule Tesla.Adapter.HttpcTest do assert data["headers"]["content-type"] == "text/plain" end + + describe "badssl" do + @describetag :integration + + test "expired.badssl.com" do + assert {:error, :econnrefused} = + Tesla.get(Tesla.client([], Tesla.Adapter.Httpc), "https://expired.badssl.com") + end + + test "wrong.host.badssl.com" do + assert {:error, :econnrefused} = + Tesla.get(Tesla.client([], Tesla.Adapter.Httpc), "https://wrong.host.badssl.com") + end + + test "self-signed.badssl.com" do + assert {:error, :econnrefused} = + Tesla.get(Tesla.client([], Tesla.Adapter.Httpc), "https://self-signed.badssl.com") + end + + test "untrusted-root.badssl.com" do + assert {:error, :econnrefused} = + Tesla.get( + Tesla.client([], Tesla.Adapter.Httpc), + "https://untrusted-root.badssl.com" + ) + end + + test "revoked.badssl.com" do + assert {:error, :econnrefused} = + Tesla.get(Tesla.client([], Tesla.Adapter.Httpc), "https://revoked.badssl.com") + end + end end