Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package elixir for openSUSE:Factory checked 
in at 2026-06-10 16:14:11
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/elixir (Old)
 and      /work/SRC/openSUSE:Factory/.elixir.new.2375 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "elixir"

Wed Jun 10 16:14:11 2026 rev:48 rq:1358516 version:1.20.1

Changes:
--------
--- /work/SRC/openSUSE:Factory/elixir/elixir.changes    2026-06-05 
14:58:44.056420832 +0200
+++ /work/SRC/openSUSE:Factory/.elixir.new.2375/elixir.changes  2026-06-10 
16:18:26.255585392 +0200
@@ -2 +2,7 @@
-Thu Jun  4 07:29:56 UTC 2026 - Alessio Biancalana <[email protected]>
+Wed Jun 10 08:42:34 UTC 2026 - Alessio Biancalana <[email protected]>
+
+- Upgrade to Elixir 1.20.1:
+  * Changelog available at https://hexdocs.pm/elixir/1.20.1/changelog.html
+
+-------------------------------------------------------------------
+Thu Jun  4 07:29:56 UTC 2026 - Alessio Biancalana <[email protected]>

Old:
----
  elixir-1.20.0-doc.zip
  elixir-1.20.0.tar.gz

New:
----
  elixir-1.20.1-doc.zip
  elixir-1.20.1.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ elixir.spec ++++++
--- /var/tmp/diff_new_pack.AKbFkw/_old  2026-06-10 16:18:27.391632471 +0200
+++ /var/tmp/diff_new_pack.AKbFkw/_new  2026-06-10 16:18:27.395632637 +0200
@@ -18,7 +18,7 @@
 
 %define elixirdir %{_prefix}/lib/elixir
 Name:           elixir
-Version:        1.20.0
+Version:        1.20.1
 Release:        0
 Summary:        Functional meta-programming aware language built atop Erlang
 License:        Apache-2.0

++++++ _scmsync.obsinfo ++++++
--- /var/tmp/diff_new_pack.AKbFkw/_old  2026-06-10 16:18:27.459635289 +0200
+++ /var/tmp/diff_new_pack.AKbFkw/_new  2026-06-10 16:18:27.463635455 +0200
@@ -1,6 +1,6 @@
-mtime: 1780558328
-commit: 1df83241ccf5af42b9acba4f760cdcdb88630521d67cc62256e92853b2dfc7b8
+mtime: 1781083285
+commit: 4c13afaf16a26b18750f65ca479305232b7167320cc27a1c129018bce9657f6a
 url: https://src.opensuse.org/erlang/elixir
-revision: 1df83241ccf5af42b9acba4f760cdcdb88630521d67cc62256e92853b2dfc7b8
+revision: 4c13afaf16a26b18750f65ca479305232b7167320cc27a1c129018bce9657f6a
 projectscmsync: https://src.opensuse.org/erlang/_ObsPrj.git
 

++++++ build.specials.obscpio ++++++

++++++ build.specials.obscpio ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/.gitignore new/.gitignore
--- old/.gitignore      1970-01-01 01:00:00.000000000 +0100
+++ new/.gitignore      2026-06-10 11:21:25.000000000 +0200
@@ -0,0 +1 @@
+.osc

++++++ elixir-1.20.0-doc.zip -> elixir-1.20.1-doc.zip ++++++
/work/SRC/openSUSE:Factory/elixir/elixir-1.20.0-doc.zip 
/work/SRC/openSUSE:Factory/.elixir.new.2375/elixir-1.20.1-doc.zip differ: char 
11, line 1

++++++ elixir-1.20.0.tar.gz -> elixir-1.20.1.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/elixir-1.20.0/CHANGELOG.md 
new/elixir-1.20.1/CHANGELOG.md
--- old/elixir-1.20.0/CHANGELOG.md      2026-06-03 19:38:11.000000000 +0200
+++ new/elixir-1.20.1/CHANGELOG.md      2026-06-09 15:36:40.000000000 +0200
@@ -192,6 +192,27 @@
 
 You can enable it by setting `elixirc_options: [module_definition: 
:interpreted]` in your `mix.exs`.
 
+## v1.20.1 (2026-06-09)
+
+### 1. Security
+
+#### Elixir
+
+  * [Version] Limit integer components in Version to 14 decimal bytes, to 
avoid parsing too large integers from untrusted user input. We strongly advise 
developers parsing versions from user input to limit the data size given to the 
`Version` module (CVE-2026-49762, GHSA-w2h8-8x3g-278p)
+
+### 2. Bug fixes
+
+#### Elixir
+
+  * [Calendar] Cap width in `Calendar.strftime/2` to 1024 characters
+  * [Code] Ensure `Code.require_file` releases the file if compilation fails
+  * [Kernel] Fix documentation generation to use the correct version in search
+
+#### Mix
+
+  * [mix archive.install] Validate paths and files when extracting archives
+  * [mix format] Honor `--no-compile` option when loading plugins
+
 ## v1.20.0 (2026-06-03)
 
 This release requires Erlang/OTP 27+ and is compatible with Erlang/OTP 29.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/elixir-1.20.0/VERSION new/elixir-1.20.1/VERSION
--- old/elixir-1.20.0/VERSION   2026-06-03 19:38:11.000000000 +0200
+++ new/elixir-1.20.1/VERSION   2026-06-09 15:36:40.000000000 +0200
@@ -1 +1 @@
-1.20.0
+1.20.1
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/elixir-1.20.0/bin/elixir new/elixir-1.20.1/bin/elixir
--- old/elixir-1.20.0/bin/elixir        2026-06-03 19:38:11.000000000 +0200
+++ new/elixir-1.20.1/bin/elixir        2026-06-09 15:36:40.000000000 +0200
@@ -6,7 +6,7 @@
 
 set -e
 
-ELIXIR_VERSION=1.20.0
+ELIXIR_VERSION=1.20.1
 
 if [ $# -eq 0 ] || { [ $# -eq 1 ] && { [ "$1" = "--help" ] || [ "$1" = "-h" ]; 
}; }; then
   cat <<USAGE >&2
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/elixir-1.20.0/bin/elixir.bat 
new/elixir-1.20.1/bin/elixir.bat
--- old/elixir-1.20.0/bin/elixir.bat    2026-06-03 19:38:11.000000000 +0200
+++ new/elixir-1.20.1/bin/elixir.bat    2026-06-09 15:36:40.000000000 +0200
@@ -4,7 +4,7 @@
 :: SPDX-FileCopyrightText: 2021 The Elixir Team
 :: SPDX-FileCopyrightText: 2012 Plataformatec
 
-set ELIXIR_VERSION=1.20.0
+set ELIXIR_VERSION=1.20.1
 
 if    ""%1""==""""                if ""%2""=="""" goto documentation
 if /I ""%1""==""--help""          if ""%2""=="""" goto documentation
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/elixir-1.20.0/lib/elixir/lib/calendar.ex 
new/elixir-1.20.1/lib/elixir/lib/calendar.ex
--- old/elixir-1.20.0/lib/elixir/lib/calendar.ex        2026-06-03 
19:38:11.000000000 +0200
+++ new/elixir-1.20.1/lib/elixir/lib/calendar.ex        2026-06-09 
15:36:40.000000000 +0200
@@ -3,6 +3,8 @@
 # SPDX-FileCopyrightText: 2012 Plataformatec
 
 defmodule Calendar do
+  @strftime_max_width 1024
+
   @moduledoc """
   This module defines the responsibilities for working with
   calendars, dates, times and datetimes in Elixir.
@@ -529,6 +531,7 @@
     * `%`: indicates the start of a formatted section
     * `<padding>`: set the padding (see below)
     * `<width>`: a number indicating the minimum size of the formatted section
+      (maximum #{@strftime_max_width})
     * `<format>`: the format itself (see below)
 
   ### Accepted padding options
@@ -667,9 +670,13 @@
   end
 
   defp parse_modifiers(<<digit, rest::binary>>, width, pad, parser_data) when 
digit in ?0..?9 do
-    new_width = (width || 0) * 10 + (digit - ?0)
+    width = (width || 0) * 10 + (digit - ?0)
+
+    if width > @strftime_max_width do
+      raise ArgumentError, "invalid strftime format: width must be at most 
#{@strftime_max_width}"
+    end
 
-    parse_modifiers(rest, new_width, pad, parser_data)
+    parse_modifiers(rest, width, pad, parser_data)
   end
 
   # set default padding if none was specified
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/elixir-1.20.0/lib/elixir/lib/code/fragment.ex 
new/elixir-1.20.1/lib/elixir/lib/code/fragment.ex
--- old/elixir-1.20.0/lib/elixir/lib/code/fragment.ex   2026-06-03 
19:38:11.000000000 +0200
+++ new/elixir-1.20.1/lib/elixir/lib/code/fragment.ex   2026-06-09 
15:36:40.000000000 +0200
@@ -204,6 +204,7 @@
           | {:local_arity, charlist}
           | {:local_call, charlist}
           | {:anonymous_call, inside_caller}
+          | {:capture_arg, charlist}
           | {:module_attribute, charlist}
           | {:operator, charlist}
           | {:operator_arity, charlist}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/elixir-1.20.0/lib/elixir/lib/code.ex 
new/elixir-1.20.1/lib/elixir/lib/code.ex
--- old/elixir-1.20.0/lib/elixir/lib/code.ex    2026-06-03 19:38:11.000000000 
+0200
+++ new/elixir-1.20.1/lib/elixir/lib/code.ex    2026-06-09 15:36:40.000000000 
+0200
@@ -1634,13 +1634,19 @@
         nil
 
       :proceed ->
-        loaded =
-          Module.ParallelChecker.verify(fn ->
-            :elixir_compiler.string(charlist, file, fn _, _ -> :ok end)
-          end)
+        try do
+          loaded =
+            Module.ParallelChecker.verify(fn ->
+              :elixir_compiler.string(charlist, file, fn _, _ -> :ok end)
+            end)
 
-        :elixir_code_server.cast({:required, file})
-        loaded
+          :elixir_code_server.cast({:required, file})
+          loaded
+        catch
+          kind, reason ->
+            :elixir_code_server.call({:release, file})
+            :erlang.raise(kind, reason, __STACKTRACE__)
+        end
     end
   end
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/elixir-1.20.0/lib/elixir/lib/uri.ex 
new/elixir-1.20.1/lib/elixir/lib/uri.ex
--- old/elixir-1.20.0/lib/elixir/lib/uri.ex     2026-06-03 19:38:11.000000000 
+0200
+++ new/elixir-1.20.1/lib/elixir/lib/uri.ex     2026-06-09 15:36:40.000000000 
+0200
@@ -24,6 +24,12 @@
 
       [scheme]://[userinfo]@[host]:[port][path]?[query]#[fragment]
 
+  The fields contain the encoded URI components as they appear in the URI
+  itself. For example, a slash inside the userinfo must be stored as `%2F`,
+  not as `/`. Functions such as `parse/1` and `new/1` preserve existing
+  percent-encoded sequences in those fields, and functions such as 
`to_string/1`
+  expects those fields to already be encoded as needed. Whenever setting or
+  modifying the fields directly, you must encode them accordingly.
 
   Note the `authority` field is deprecated. `parse/1` will still
   populate it for backwards compatibility but you should generally
@@ -897,6 +903,9 @@
   @doc """
   Returns the string representation of the given [URI struct](`t:t/0`).
 
+  This function assembles the URI components into a string, assuming each
+  field is valid and escaped as done by `parse/1` and `new/1`.
+
   ## Examples
 
       iex> uri = URI.parse("http://google.com";)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/elixir-1.20.0/lib/elixir/lib/version.ex 
new/elixir-1.20.1/lib/elixir/lib/version.ex
--- old/elixir-1.20.0/lib/elixir/lib/version.ex 2026-06-03 19:38:11.000000000 
+0200
+++ new/elixir-1.20.1/lib/elixir/lib/version.ex 2026-06-09 15:36:40.000000000 
+0200
@@ -18,12 +18,16 @@
 
       MAJOR.MINOR.PATCH
 
+  Each numeric component is limited to at most 14 digits.
+
   Pre-releases are supported by optionally appending a hyphen and a series of
   period-separated identifiers immediately following the patch version.
   Identifiers consist of only ASCII alphanumeric characters and hyphens 
(`[0-9A-Za-z-]`):
 
       "1.0.0-alpha.3"
 
+  Numeric pre-release identifiers are also limited to at most 14 digits.
+
   Build information can be added by appending a plus sign and a series of
   dot-separated identifiers immediately following the patch or pre-release 
version.
   Identifiers consist of only ASCII alphanumeric characters and hyphens 
(`[0-9A-Za-z-]`):
@@ -520,6 +524,8 @@
   defmodule Parser do
     @moduledoc false
 
+    @max_numeric_component_digits 14
+
     operators = [
       {">=", :>=},
       {"<=", :<=},
@@ -621,7 +627,9 @@
     defp require_digits(nil), do: :error
 
     defp require_digits(string) do
-      if leading_zero?(string), do: :error, else: parse_digits(string, "")
+      if leading_zero?(string) or byte_size(string) > 
@max_numeric_component_digits,
+        do: :error,
+        else: parse_digits(string, "")
     end
 
     defp leading_zero?(<<?0, _, _::binary>>), do: true
@@ -649,6 +657,11 @@
       end
     end
 
+    defp convert_parts_to_integer([part | rest], acc)
+         when byte_size(part) > @max_numeric_component_digits do
+      if all_digits?(part), do: :error, else: convert_parts_to_integer(rest, 
[part | acc])
+    end
+
     defp convert_parts_to_integer([part | rest], acc) do
       case parse_digits(part, "") do
         {:ok, integer} ->
@@ -667,6 +680,10 @@
       {:ok, Enum.reverse(acc)}
     end
 
+    defp all_digits?(<<char, rest::binary>>) when char in ?0..?9, do: 
all_digits?(rest)
+    defp all_digits?(<<>>), do: true
+    defp all_digits?(_other), do: false
+
     defp valid_identifier?(<<char, rest::binary>>)
          when char in ?0..?9
          when char in ?a..?z
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/elixir-1.20.0/lib/elixir/scripts/elixir_docs.exs 
new/elixir-1.20.1/lib/elixir/scripts/elixir_docs.exs
--- old/elixir-1.20.0/lib/elixir/scripts/elixir_docs.exs        2026-06-03 
19:38:11.000000000 +0200
+++ new/elixir-1.20.1/lib/elixir/scripts/elixir_docs.exs        2026-06-09 
15:36:40.000000000 +0200
@@ -3,7 +3,11 @@
 # SPDX-FileCopyrightText: 2012 Plataformatec
 
 # Returns config for Elixir docs (exclusively)
-canonical = System.fetch_env!("CANONICAL")
+canonical =
+  case System.fetch_env!("CANONICAL") do
+    "" -> System.version() <> "/"
+    canonical -> canonical
+  end
 
 [
   search: [
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/elixir-1.20.0/lib/elixir/scripts/mix_docs.exs 
new/elixir-1.20.1/lib/elixir/scripts/mix_docs.exs
--- old/elixir-1.20.0/lib/elixir/scripts/mix_docs.exs   2026-06-03 
19:38:11.000000000 +0200
+++ new/elixir-1.20.1/lib/elixir/scripts/mix_docs.exs   2026-06-09 
15:36:40.000000000 +0200
@@ -2,7 +2,11 @@
 # SPDX-FileCopyrightText: 2021 The Elixir Team
 
 # Returns config for other apps except Elixir
-canonical = System.fetch_env!("CANONICAL")
+canonical =
+  case System.fetch_env!("CANONICAL") do
+    "" -> System.version() <> "/"
+    canonical -> canonical
+  end
 
 [
   search: [
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/elixir-1.20.0/lib/elixir/src/elixir_code_server.erl 
new/elixir-1.20.1/lib/elixir/src/elixir_code_server.erl
--- old/elixir-1.20.0/lib/elixir/src/elixir_code_server.erl     2026-06-03 
19:38:11.000000000 +0200
+++ new/elixir-1.20.1/lib/elixir/src/elixir_code_server.erl     2026-06-09 
15:36:40.000000000 +0200
@@ -59,6 +59,9 @@
 handle_call(required, _From, Config) ->
   {reply, [F || {F, true} <- 
maps:to_list(Config#elixir_code_server.required)], Config};
 
+handle_call({release, Path}, _From, Config) ->
+  {reply, ok, release(Path, Config)};
+
 handle_call(retrieve_compiler_module, _From, Config) ->
   case Config#elixir_code_server.mod_pool of
     {Used, [Mod | Unused], Counter} ->
@@ -140,6 +143,20 @@
 code_change(_Old, Config, _Extra) ->
   {ok, Config}.
 
+release(Path, Config) ->
+  Current = Config#elixir_code_server.required,
+  case maps:find(Path, Current) of
+    {ok, []} ->
+      Released = maps:remove(Path, Current),
+      Config#elixir_code_server{required=Released};
+    {ok, [Next | Waiting]} ->
+      _ = gen_server:reply(Next, proceed),
+      Released = maps:put(Path, Waiting, Current),
+      Config#elixir_code_server{required=Released};
+    error ->
+      Config
+  end.
+
 compiler_module(I) ->
   list_to_atom("elixir_compiler_" ++ integer_to_list(I)).
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/elixir-1.20.0/lib/elixir/src/elixir_utils.erl 
new/elixir-1.20.1/lib/elixir/src/elixir_utils.erl
--- old/elixir-1.20.0/lib/elixir/src/elixir_utils.erl   2026-06-03 
19:38:11.000000000 +0200
+++ new/elixir-1.20.1/lib/elixir/src/elixir_utils.erl   2026-06-09 
15:36:40.000000000 +0200
@@ -233,6 +233,9 @@
 returns_boolean({{'.', _, [erlang, Fun]}, _, [_, _, _]}) when
   Fun == function_exported; Fun == is_record -> true;
 
+returns_boolean({{'.', _, [lists, member]}, _, [_, _]}) ->
+  true;
+
 returns_boolean({'case', _, [_, [{do, Clauses}]]}) ->
   lists:all(fun
     ({'->', _, [_, Expr]}) -> returns_boolean(Expr)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/elixir-1.20.0/lib/elixir/test/elixir/calendar_test.exs 
new/elixir-1.20.1/lib/elixir/test/elixir/calendar_test.exs
--- old/elixir-1.20.0/lib/elixir/test/elixir/calendar_test.exs  2026-06-03 
19:38:11.000000000 +0200
+++ new/elixir-1.20.1/lib/elixir/test/elixir/calendar_test.exs  2026-06-09 
15:36:40.000000000 +0200
@@ -340,6 +340,32 @@
       assert Calendar.strftime(~N[2019-08-15 17:07:57], "%010A") == 
"00Thursday"
     end
 
+    test "limits width to at most 1024 characters" do
+      assert Calendar.strftime(~D[2019-08-15], "%1024d") |> byte_size() == 1024
+
+      assert_raise ArgumentError, "invalid strftime format: width must be at 
most 1024", fn ->
+        Calendar.strftime(~D[2019-08-15], "%1025d")
+      end
+
+      assert_raise ArgumentError, "invalid strftime format: width must be at 
most 1024", fn ->
+        Calendar.strftime(~D[2019-08-15], "%10000d")
+      end
+    end
+
+    test "limits width in preferred formats" do
+      assert_raise ArgumentError, "invalid strftime format: width must be at 
most 1024", fn ->
+        Calendar.strftime(~N[2019-08-15 17:07:57], "%c", preferred_datetime: 
"%1025d")
+      end
+
+      assert_raise ArgumentError, "invalid strftime format: width must be at 
most 1024", fn ->
+        Calendar.strftime(~N[2019-08-15 17:07:57], "%x", preferred_date: 
"%1025d")
+      end
+
+      assert_raise ArgumentError, "invalid strftime format: width must be at 
most 1024", fn ->
+        Calendar.strftime(~N[2019-08-15 17:07:57], "%X", preferred_time: 
"%1025H")
+      end
+    end
+
     test "formats Epoch time with %s" do
       assert Calendar.strftime(~N[2019-08-15 17:07:57], "%s") == "1565888877"
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/elixir-1.20.0/lib/elixir/test/elixir/code_test.exs 
new/elixir-1.20.1/lib/elixir/test/elixir/code_test.exs
--- old/elixir-1.20.0/lib/elixir/test/elixir/code_test.exs      2026-06-03 
19:38:11.000000000 +0200
+++ new/elixir-1.20.1/lib/elixir/test/elixir/code_test.exs      2026-06-09 
15:36:40.000000000 +0200
@@ -514,6 +514,25 @@
     Code.unrequire_files([fixture_path("code_sample.exs")])
   end
 
+  test "require_file/1 releases the file when compilation fails" do
+    path = tmp_path("bad_require_#{System.unique_integer([:positive])}.ex")
+
+    try do
+      File.write!(path, ~s|raise "boom"|)
+
+      assert_raise RuntimeError, "boom", fn ->
+        Code.require_file(path)
+      end
+
+      assert_raise RuntimeError, "boom", fn ->
+        Code.require_file(path)
+      end
+    after
+      File.rm(path)
+      Code.unrequire_files([path])
+    end
+  end
+
   test "string_to_quoted!/2 errors take lines/columns/indentation into 
account" do
     assert_exception(
       SyntaxError,
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/elixir-1.20.0/lib/elixir/test/elixir/version_test.exs 
new/elixir-1.20.1/lib/elixir/test/elixir/version_test.exs
--- old/elixir-1.20.0/lib/elixir/test/elixir/version_test.exs   2026-06-03 
19:38:11.000000000 +0200
+++ new/elixir-1.20.1/lib/elixir/test/elixir/version_test.exs   2026-06-09 
15:36:40.000000000 +0200
@@ -84,9 +84,15 @@
     assert {:ok, %Version{major: 1, minor: 4, patch: 5, pre: [6, 7, "eight"]}} 
=
              Version.parse("1.4.5-6.7.eight")
 
+    assert {:ok, %Version{major: 99_999_999_999_999, minor: 0, patch: 0}} =
+             Version.parse("99999999999999.0.0")
+
     assert {:ok, %Version{major: 1, minor: 4, patch: 5, pre: ["6-g3318bd5"]}} =
              Version.parse("1.4.5-6-g3318bd5+ignore")
 
+    assert {:ok, %Version{major: 1, minor: 0, patch: 0, pre: 
["100000000000000-alpha"]}} =
+             Version.parse("1.0.0-100000000000000-alpha")
+
     assert Version.parse("foobar") == :error
     assert Version.parse("2") == :error
     assert Version.parse("2.") == :error
@@ -105,6 +111,13 @@
     assert Version.parse("02.3.0") == :error
     assert Version.parse("0. 0.0") == :error
     assert Version.parse("0.1.0-&&pre") == :error
+    assert Version.parse("100000000000000.0.0") == :error
+    assert Version.parse("1.100000000000000.0") == :error
+    assert Version.parse("1.0.100000000000000") == :error
+    assert Version.parse("1.0.0-100000000000000") == :error
+
+    assert Version.parse("1.0.0+100000000000000") ==
+             {:ok, %Version{major: 1, minor: 0, patch: 0, build: 
"100000000000000"}}
   end
 
   test "to_string/1" do
@@ -338,6 +351,7 @@
       assert Version.parse_requirement("1.2.3 and or 4.5.6") == :error
       assert Version.parse_requirement(">= 1") == :error
       assert Version.parse_requirement("1.2.3 >=") == :error
+      assert Version.parse_requirement("100000000000000.0.0") == :error
     end
 
     test "inspect/1" do
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/elixir-1.20.0/lib/mix/lib/mix/local/installer.ex 
new/elixir-1.20.1/lib/mix/lib/mix/local/installer.ex
--- old/elixir-1.20.0/lib/mix/lib/mix/local/installer.ex        2026-06-03 
19:38:11.000000000 +0200
+++ new/elixir-1.20.1/lib/mix/lib/mix/local/installer.ex        2026-06-09 
15:36:40.000000000 +0200
@@ -181,15 +181,17 @@
     message =
       case previous_files do
         [] ->
-          "Are you sure you want to install #{inspect(src)}?"
+          "Do you trust and want to install #{inspect(src)}?"
 
         [file] ->
           "Found existing entry: #{file}\n" <>
-            "Are you sure you want to replace it with #{inspect(src)}?"
+            "The existing entry will be replaced.\n" <>
+            "Do you trust and want to install #{inspect(src)}?"
 
         files ->
           "Found existing entries: #{Enum.map_join(files, ", ", 
&Path.basename/1)}\n" <>
-            "Are you sure you want to replace them with #{inspect(src)}?"
+            "The existing entries will be replaced.\n" <>
+            "Do you trust and want to install #{inspect(src)}?"
       end
 
     Mix.shell().yes?(message)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/elixir-1.20.0/lib/mix/lib/mix/tasks/archive.install.ex 
new/elixir-1.20.1/lib/mix/lib/mix/tasks/archive.install.ex
--- old/elixir-1.20.0/lib/mix/lib/mix/tasks/archive.install.ex  2026-06-03 
19:38:11.000000000 +0200
+++ new/elixir-1.20.1/lib/mix/lib/mix/tasks/archive.install.ex  2026-06-09 
15:36:40.000000000 +0200
@@ -40,6 +40,15 @@
   Note that installing via Git, GitHub, or Hex fetches the source
   of the archive and builds it, while using local path uses a pre-built 
archive.
 
+  ## Security
+
+  Archives must be installed only from sources you trust.
+
+  Installing an archive from Git, GitHub, or Hex executes code from the source
+  during installation, unless a pre-built archive is given. Once an archive is
+  installed, Mix may load code from it as a plugin on any Mix command, even if
+  no archive command is executed.
+
   ## Command line options
 
     * `--sha512` - checks the archive matches the given SHA-512 checksum. Only
@@ -119,7 +128,8 @@
   @impl true
   def install(basename, contents, previous) do
     ez_path = Path.join(Mix.path_for(:archives), basename)
-    dir_dest = resolve_destination(ez_path, contents)
+    archive_name = archive_name!(contents)
+    dir_dest = Path.join(Path.dirname(ez_path), archive_name)
 
     remove_previous_versions(previous)
 
@@ -149,17 +159,62 @@
 
   ### Private helpers
 
-  defp resolve_destination(ez_path, contents) do
-    with {:ok, [_comment, zip_first_file | _]} <- :zip.list_dir(contents),
-         {:zip_file, zip_first_path, _, _, _, _} = zip_first_file,
-         [zip_root_dir | _] = Path.split(zip_first_path) do
-      Path.join(Path.dirname(ez_path), zip_root_dir)
+  defp archive_name!(contents) do
+    with {:ok, files} <- :zip.list_dir(contents),
+         zip_files = Enum.filter(files, &match?({:zip_file, _, _, _, _, _}, 
&1)),
+         true <- zip_files != [] do
+      Enum.reduce(zip_files, nil, fn zip_file, root ->
+        validate_archive_path!(zip_file, root)
+      end)
     else
       _ ->
-        Mix.raise("Installation failed: invalid archive file")
+        Mix.raise("Installation failed: invalid archive file, no files found")
+    end
+  end
+
+  defp validate_archive_path!({:zip_file, path, file_info, _, _, _}, root) do
+    type = elem(file_info, 2)
+    path = zip_path_to_string(path)
+
+    unless type in [:regular, :directory] do
+      Mix.raise(
+        "Installation failed: invalid archive file, #{inspect(path)} is not a 
regular file or directory"
+      )
+    end
+
+    cond do
+      Path.type(path) != :relative ->
+        Mix.raise(
+          "Installation failed: invalid archive file, #{inspect(path)} is an 
absolute path"
+        )
+
+      String.contains?(path, ["..", "\\", <<0>>]) ->
+        Mix.raise("Installation failed: invalid archive file, #{inspect(path)} 
is an unsafe path")
+
+      true ->
+        :ok
+    end
+
+    case String.split(path, "/", trim: true) do
+      [new_root | _] ->
+        cond do
+          root && root != new_root ->
+            Mix.raise(
+              "Installation failed: invalid archive file, #{inspect(path)} is 
outside archive root #{inspect(root)}"
+            )
+
+          true ->
+            new_root
+        end
+
+      [] ->
+        Mix.raise("Installation failed: invalid archive file, #{inspect(path)} 
is empty")
     end
   end
 
+  defp zip_path_to_string(path) when is_list(path), do: List.to_string(path)
+  defp zip_path_to_string(path) when is_binary(path), do: path
+
   defp archives(name) do
     Mix.path_for(:archives)
     |> Path.join(name)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/elixir-1.20.0/lib/mix/lib/mix/tasks/escript.install.ex 
new/elixir-1.20.1/lib/mix/lib/mix/tasks/escript.install.ex
--- old/elixir-1.20.0/lib/mix/lib/mix/tasks/escript.install.ex  2026-06-03 
19:38:11.000000000 +0200
+++ new/elixir-1.20.1/lib/mix/lib/mix/tasks/escript.install.ex  2026-06-09 
15:36:40.000000000 +0200
@@ -40,6 +40,14 @@
   `$PATH` environment variable. For more information, check the wikipedia
   article on PATH: https://en.wikipedia.org/wiki/PATH_(variable)
 
+  ## Security
+
+  Escripts must be installed only from sources you trust.
+
+  Installing an escript from Git, GitHub, or Hex executes code from the source
+  during installation, unless a pre-built escript is given. Once an escript is
+  installed, running it executes code on your machine.
+
   ## Command line options
 
     * `--sha512` - checks the escript matches the given SHA-512 checksum. Only
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/elixir-1.20.0/lib/mix/lib/mix/tasks/format.ex 
new/elixir-1.20.1/lib/mix/lib/mix/tasks/format.ex
--- old/elixir-1.20.0/lib/mix/lib/mix/tasks/format.ex   2026-06-03 
19:38:11.000000000 +0200
+++ new/elixir-1.20.1/lib/mix/lib/mix/tasks/format.ex   2026-06-09 
15:36:40.000000000 +0200
@@ -78,6 +78,10 @@
 
     * `--dry-run` - does not save files after formatting.
 
+    * `--no-compile` - does not compile, even if compilation is required
+      to load formatter plugins. If a plugin cannot be loaded, an error
+      is raised.
+
     * `--verbose` - prints the names of files that were formatted.
 
     * `--dot-formatter` - path to the file with formatter configuration.
@@ -152,7 +156,7 @@
       ]
 
   Notice that, when running the formatter with plugins, your code will be
-  compiled first.
+  compiled first, unless the `--no-compile` flag is given.
 
   In addition, the order by which you input your plugins is the format order.
   So, in the above `.formatter.exs`, the `MixMarkdownFormatter` will format
@@ -316,7 +320,7 @@
 
     plugins =
       if plugins != [] do
-        Keyword.get(opts, :plugin_loader, &plugin_loader/1).(plugins)
+        Keyword.get(opts, :plugin_loader, &plugin_loader(&1, opts)).(plugins)
       else
         []
       end
@@ -357,12 +361,12 @@
      end)}
   end
 
-  defp plugin_loader(plugins) do
+  defp plugin_loader(plugins, opts) do
     if plugins != [] do
-      Mix.Task.run("loadpaths", [])
+      Mix.Task.run("loadpaths", if(opts[:no_compile], do: ["--no-compile"], 
else: []))
     end
 
-    if not Enum.all?(plugins, &Code.ensure_loaded?/1) do
+    if !opts[:no_compile] and not Enum.all?(plugins, &Code.ensure_loaded?/1) do
       Mix.Task.run("compile", [])
     end
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/elixir-1.20.0/lib/mix/test/mix/tasks/archive_test.exs 
new/elixir-1.20.1/lib/mix/test/mix/tasks/archive_test.exs
--- old/elixir-1.20.0/lib/mix/test/mix/tasks/archive_test.exs   2026-06-03 
19:38:11.000000000 +0200
+++ new/elixir-1.20.1/lib/mix/test/mix/tasks/archive_test.exs   2026-06-09 
15:36:40.000000000 +0200
@@ -119,6 +119,45 @@
     end)
   end
 
+  test "archive install rejects parent directory entries" do
+    in_tmp("archive install rejects parent directory entries", fn ->
+      assert {:ok, _} =
+               :zip.create(~c"bad-0.1.0.ez", [
+                 {~c"../outside", "bad"},
+                 {~c"bad-0.1.0/ebin/bad", "bad"}
+               ])
+
+      send(self(), {:mix_shell_input, :yes?, true})
+
+      assert_raise Mix.Error, ~r/invalid archive file/, fn ->
+        Mix.Tasks.Archive.Install.run(["bad-0.1.0.ez"])
+      end
+
+      refute File.exists?(tmp_path("userhome/outside"))
+      refute File.exists?(tmp_path("userhome/.mix/outside"))
+      refute File.dir?(tmp_path("userhome/.mix/archives/bad-0.1.0"))
+    end)
+  end
+
+  test "archive install rejects entries outside the archive root" do
+    in_tmp("archive install rejects entries outside the archive root", fn ->
+      assert {:ok, _} =
+               :zip.create(~c"bad-0.1.0.ez", [
+                 {~c"bad-0.1.0/ebin/bad", "bad"},
+                 {~c"other-0.1.0/ebin/bad", "bad"}
+               ])
+
+      send(self(), {:mix_shell_input, :yes?, true})
+
+      assert_raise Mix.Error, ~r/invalid archive file/, fn ->
+        Mix.Tasks.Archive.Install.run(["bad-0.1.0.ez"])
+      end
+
+      refute File.dir?(tmp_path("userhome/.mix/archives/bad-0.1.0"))
+      refute File.dir?(tmp_path("userhome/.mix/archives/other-0.1.0"))
+    end)
+  end
+
   test "archive install missing file" do
     message = ~r[Expected "./unlikely-to-exist-0.1.0.ez" to be a local file 
path]
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/elixir-1.20.0/lib/mix/test/mix/tasks/format_test.exs 
new/elixir-1.20.1/lib/mix/test/mix/tasks/format_test.exs
--- old/elixir-1.20.0/lib/mix/test/mix/tasks/format_test.exs    2026-06-03 
19:38:11.000000000 +0200
+++ new/elixir-1.20.1/lib/mix/test/mix/tasks/format_test.exs    2026-06-09 
15:36:40.000000000 +0200
@@ -597,6 +597,51 @@
     end)
   end
 
+  defmodule FormatWithPluginApp do
+    def project do
+      [app: :format_with_plugin, version: "0.1.0"]
+    end
+  end
+
+  test "doesn't compile plugins with --no-compile", context do
+    in_tmp(context.test, fn ->
+      Mix.Project.push(__MODULE__.FormatWithPluginApp)
+      on_exit(fn -> purge([UncompiledPlugin]) end)
+
+      File.write!(".formatter.exs", """
+      [
+        inputs: ["a.ex"],
+        plugins: [UncompiledPlugin]
+      ]
+      """)
+
+      File.mkdir_p!("lib")
+
+      File.write!("lib/uncompiled_plugin.ex", """
+      defmodule UncompiledPlugin do
+        @behaviour Mix.Tasks.Format
+
+        def features(_opts), do: [extensions: [".ex"]]
+        def format(contents, _opts), do: "# formatted\\n" <> contents
+      end
+      """)
+
+      File.write!("a.ex", """
+      foo bar
+      """)
+
+      assert_raise Mix.Error, "Formatter plugin UncompiledPlugin cannot be 
found", fn ->
+        Mix.Tasks.Format.run(["--no-compile"])
+      end
+
+      refute_received {:mix_shell, :info, ["Compiling" <> _]}
+
+      assert File.read!("a.ex") == """
+             foo bar
+             """
+    end)
+  end
+
   test "uses extension plugins with --stdin-filename", context do
     in_tmp(context.test, fn ->
       File.write!(".formatter.exs", """

Reply via email to