1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
|
From 206d3a93a6e06b57c3d9da1b2fd173b81a4b4c47 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jos=C3=A9=20Valim?= <jose.valim@gmail.com>
Date: Tue, 9 Jun 2026 15:25:57 +0200
Subject: [PATCH 3/3] Limit version numbers to 14 bytes
This avoids parsing too large integers.
CVE-2026-49762
GHSA-w2h8-8x3g-278p
---
lib/elixir/lib/version.ex | 19 ++++++++++++++++++-
lib/elixir/test/elixir/version_test.exs | 14 ++++++++++++++
2 files changed, 32 insertions(+), 1 deletion(-)
diff --git a/lib/elixir/lib/version.ex b/lib/elixir/lib/version.ex
index 833faacb5..4f3f3c72a 100644
--- a/lib/elixir/lib/version.ex
+++ b/lib/elixir/lib/version.ex
@@ -14,12 +14,16 @@ defmodule Version do
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-]`):
@@ -509,6 +513,8 @@ defp pre_to_string(pre) do
defmodule Parser do
@moduledoc false
+ @max_numeric_component_digits 14
+
operators = [
{">=", :>=},
{"<=", :<=},
@@ -610,7 +616,9 @@ def parse_version(string, approximate? \\ false) when is_binary(string) do
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
@@ -638,6 +646,11 @@ defp optional_dot_separated(string) do
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} ->
@@ -656,6 +669,10 @@ defp convert_parts_to_integer([], acc) do
{: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 --git a/lib/elixir/test/elixir/version_test.exs b/lib/elixir/test/elixir/version_test.exs
index c960c3f05..d7b1f8d66 100644
--- a/lib/elixir/test/elixir/version_test.exs
+++ b/lib/elixir/test/elixir/version_test.exs
@@ -80,9 +80,15 @@ test "parse/1" do
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
@@ -101,6 +107,13 @@ test "parse/1" do
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
@@ -334,5 +347,6 @@ test "compile requirement" do
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
end
--
2.52.0
|