Skip to content

Commit c87f768

Browse files
committed
Fix a ReDoS vulnerability in URI template matching
1 parent 0d7e9b2 commit c87f768

File tree

6 files changed

+27
-7
lines changed

6 files changed

+27
-7
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,4 @@ pkg
1010
specdoc
1111
tmp/
1212
vendor/
13+
.claude/settings.local.json

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
# Changelog
22

3+
## Addressable 2.8.10 <a name="v2.8.10">
4+
- fixes ReDoS vulnerability in Addressable::Template#match
5+
36
## Addressable 2.8.9 <a name="v2.8.9">
47
- Reduce gem size by excluding test files ([#569])
58
- No need for bundler as development dependency ([#571], [5fc1d93](https://github.com/sporkmonger/addressable/commit/5fc1d93))

addressable.gemspec

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
# -*- encoding: utf-8 -*-
2-
# stub: addressable 2.8.9 ruby lib
2+
# stub: addressable 2.8.10 ruby lib
33

44
Gem::Specification.new do |s|
55
s.name = "addressable".freeze
6-
s.version = "2.8.9".freeze
6+
s.version = "2.8.10".freeze
77

88
s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version=
9-
s.metadata = { "changelog_uri" => "https://github.com/sporkmonger/addressable/blob/main/CHANGELOG.md#v2.8.9" } if s.respond_to? :metadata=
9+
s.metadata = { "changelog_uri" => "https://github.com/sporkmonger/addressable/blob/main/CHANGELOG.md#v2.8.10" } if s.respond_to? :metadata=
1010
s.require_paths = ["lib".freeze]
1111
s.authors = ["Bob Aman".freeze]
12-
s.date = "2026-02-27"
12+
s.date = "2026-04-04"
1313
s.description = "Addressable is an alternative implementation to the URI implementation that is\npart of Ruby's standard library. It is flexible, offers heuristic parsing, and\nadditionally provides extensive support for IRIs and URI templates.\n".freeze
1414
s.email = "[email protected]".freeze
1515
s.extra_rdoc_files = ["README.md".freeze]
@@ -18,7 +18,7 @@ Gem::Specification.new do |s|
1818
s.licenses = ["Apache-2.0".freeze]
1919
s.rdoc_options = ["--main".freeze, "README.md".freeze]
2020
s.required_ruby_version = Gem::Requirement.new(">= 2.2".freeze)
21-
s.rubygems_version = "4.0.7".freeze
21+
s.rubygems_version = "3.6.3".freeze
2222
s.summary = "URI Implementation".freeze
2323

2424
s.specification_version = 4

lib/addressable/template.rb

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ class Template
3939
"(?>(?:[#{variable_char_class}]|%[a-fA-F0-9][a-fA-F0-9])+)"
4040
RESERVED =
4141
"(?:[#{anything}]|%[a-fA-F0-9][a-fA-F0-9])"
42+
RESERVED_NO_COMMA =
43+
"(?:[#{anything.delete(',')}]|%[a-fA-F0-9][a-fA-F0-9])"
4244
UNRESERVED =
4345
"(?:[#{
4446
Addressable::URI::CharacterClasses::UNRESERVED
@@ -1011,7 +1013,11 @@ def parse_new_template_pattern(pattern, processor = nil)
10111013
"#{ UNRESERVED }*?"
10121014
end
10131015
if modifier == '*'
1014-
"(?<#{name}>#{group}(?:#{joiner}?#{group})*)?"
1016+
seg = case operator
1017+
when '+', '#' then "#{RESERVED_NO_COMMA}*?"
1018+
else group
1019+
end
1020+
"(?<#{name}>#{seg}(?:#{joiner}?#{seg})*)?"
10151021
else
10161022
"(?<#{name}>#{group})?"
10171023
end

lib/addressable/version.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ module Addressable
2323
module VERSION
2424
MAJOR = 2
2525
MINOR = 8
26-
TINY = 9
26+
TINY = 10
2727

2828
STRING = [MAJOR, MINOR, TINY].join('.')
2929
end

spec/addressable/template_spec.rb

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1003,6 +1003,16 @@ def self.match(name)
10031003
expect(subject.variables).to eq(["foo", "bar"])
10041004
end
10051005
end
1006+
context "+ operator with explode modifier" do
1007+
subject { Addressable::Template.new("{+foo*}") }
1008+
it "should match in linear time against a non-matching payload" do
1009+
expect do
1010+
Timeout.timeout(10) do
1011+
expect(subject.match(("a," * 1000) + " ")).to be_nil
1012+
end
1013+
end.not_to raise_error
1014+
end
1015+
end
10061016
context ". operator" do
10071017
subject { Addressable::Template.new("foo{.foo,bar}baz") }
10081018
it "can match" do

0 commit comments

Comments
 (0)