CVE-2017-20166: Other (NVD-CWE-Other)
value: Ecto 2.2.0 lacks a certain protection mechanism associated with the interaction between is_nil and raise.
CWE description: NVD is only using a subset of CWE for mapping instead of the entire CWE, and the weakness type is not covered by that subset.
CVSS v3 base score: 9.8
Published: 2023-01-10T06:15Z
Repository: https://github.com/elixir-ecto/ecto
Language: Other
Files changed: integration_test/cases/repo.exs, lib/ecto/repo/queryable.ex, test/ecto/repo_test.exs
Commit date: 2017-08-27 18:46:34 +0200
Commit message: Revert "Add support for clauses with nil values in `Repo.get_by(!)/2` (#2125)"

This reverts commit 8e1b378a6f79b0f26495578e57f896d3361c808a.

Diff: diff --git a/integration_test/cases/repo.exs b/integration_test/cases/repo.exs
index d743a9d4..0fa8c5b4 100644
--- a/integration_test/cases/repo.exs
+++ b/integration_test/cases/repo.exs
@@ -599,25 +599,22 @@ defmodule Ecto.Integration.RepoTest do
   end
 
   test "get_by(!)" do
     post1 = TestRepo.insert!(%Post{title: "1", text: "hai"})
     post2 = TestRepo.insert!(%Post{title: "2", text: "hello"})
-    post3 = TestRepo.insert!(%Post{title: "3", text: nil})
 
     assert post1 == TestRepo.get_by(Post, id: post1.id)
     assert post1 == TestRepo.get_by(Post, text: post1.text)
     assert post1 == TestRepo.get_by(Post, id: post1.id, text: post1.text)
     assert post2 == TestRepo.get_by(Post, id: to_string(post2.id)) # With casting
     assert nil   == TestRepo.get_by(Post, text: "hey")
     assert nil   == TestRepo.get_by(Post, id: post2.id, text: "hey")
-    assert post3 == TestRepo.get_by(Post, text: nil)
 
     assert post1 == TestRepo.get_by!(Post, id: post1.id)
     assert post1 == TestRepo.get_by!(Post, text: post1.text)
     assert post1 == TestRepo.get_by!(Post, id: post1.id, text: post1.text)
     assert post2 == TestRepo.get_by!(Post, id: to_string(post2.id)) # With casting
-    assert post3 == TestRepo.get_by!(Post, text: nil)
 
     assert post1 == TestRepo.get_by!(Post, %{id: post1.id})
 
     assert_raise Ecto.NoResultsError, fn ->
       TestRepo.get_by!(Post, id: post2.id, text: "hey")
diff --git a/lib/ecto/repo/queryable.ex b/lib/ecto/repo/queryable.ex
index 2337d8f8..84815612 100644
--- a/lib/ecto/repo/queryable.ex
+++ b/lib/ecto/repo/queryable.ex
@@ -345,16 +345,11 @@ defmodule Ecto.Repo.Queryable do
           "to have exactly one primary key, got: #{inspect pks}"
     end
   end
 
   defp query_for_get_by(_repo, queryable, clauses) do
-    Enum.reduce(clauses, queryable, fn
-      ({key, nil}, query) ->
-        Query.where(query, [x], is_nil(field(x, ^key)))
-      ({key, value}, query) ->
-        Query.where(query, [x], field(x, ^key) == ^value)
-    end)
+    Query.where(queryable, [], ^Enum.to_list(clauses))
   end
 
   defp query_for_aggregate(queryable, aggregate, field) do
     query = %{Queryable.to_query(queryable) | preloads: [], assocs: []}
     ast   = field(0, field)
diff --git a/test/ecto/repo_test.exs b/test/ecto/repo_test.exs
index 97e9ea46..837c4154 100644
--- a/test/ecto/repo_test.exs
+++ b/test/ecto/repo_test.exs
@@ -110,11 +110,10 @@ defmodule Ecto.RepoTest do
   end
 
   test "validates get_by" do
     TestRepo.get_by(MySchema, id: 123)
     TestRepo.get_by(MySchema, %{id: 123})
-    TestRepo.get_by(MySchema, id: nil)
 
     message = ~r"value `:atom` in `where` cannot be cast to type :id in query"
     assert_raise Ecto.Query.CastError, message, fn ->
       TestRepo.get_by(MySchema, id: :atom)
     end

Vulnerable code:     post3 = TestRepo.insert!(%Post{title: "3", text: nil})
    assert post3 == TestRepo.get_by(Post, text: nil)
    assert post3 == TestRepo.get_by!(Post, text: nil)
    Enum.reduce(clauses, queryable, fn
      ({key, nil}, query) ->
        Query.where(query, [x], is_nil(field(x, ^key)))
      ({key, value}, query) ->
        Query.where(query, [x], field(x, ^key) == ^value)
    end)
    TestRepo.get_by(MySchema, id: nil)
Fixed code:     Query.where(queryable, [], ^Enum.to_list(clauses))