You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
require"addressable"require"benchmark"puts"*" * 30puts"path with expansion - {scheme}://{host}{/path*}{?query*}"puts"*" * 30template=Addressable::Template.new("{scheme}://{host}{/path*}{?query*}")puts"with one part"putsBenchmark.measure{putstemplate.extract("http://host.com/abcdefghijklmnopqrstuvwxyz")}puts"with one part and empty query - reDDOS"putsBenchmark.measure{putstemplate.extract("http://host.com/abcdefghijklmnopqrstuvwxyz?a")}puts"expand with one part"putsBenchmark.measure{putstemplate.expand({scheme: "https",host: "host.com",path: "abcdefghijklmnopqrstuvwxyz",})}puts"expand with one part and empty query"putsBenchmark.measure{putstemplate.expand({scheme: "https",host: "host.com",path: "abcdefghijklmnopqrstuvwxyz",query: {a: nil},})}# ❥ ruby benchmark.rb# With one part# 0.000383 0.000027 0.000410 ( 0.000407)# With one part and empty query# 12.595742 0.006677 12.602419 ( 12.612499)# Expand with one part# 0.000219 0.000003 0.000222 ( 0.000223)# Expand with one part and empty query# 0.000177 0.000011 0.000188 ( 0.000188)puts"\n\n\n\n"template=Addressable::Template.new("{scheme}://{host}{/path}{?query*}")puts"*" * 30puts"path without expansion - {scheme}://{host}{/path}{?query*}"puts"*" * 30puts"with one part"putsBenchmark.measure{putstemplate.extract("http://host.com/abcdefghijklmnopqrstuvwxyz")}puts"with one part and empty query"putsBenchmark.measure{putstemplate.extract("http://host.com/abcdefghijklmnopqrstuvwxyz?a")}puts"expand with one part"putsBenchmark.measure{putstemplate.expand({scheme: "https",host: "host.com",path: "abcdefghijklmnopqrstuvwxyz",})}puts"expand with one part and empty query"putsBenchmark.measure{putstemplate.expand({scheme: "https",host: "host.com",path: "abcdefghijklmnopqrstuvwxyz",query: {a: nil},})}# ❥ ruby benchmark.rb# With one part# 0.000383 0.000027 0.000410 ( 0.000407)# With one part and empty query# 12.595742 0.006677 12.602419 ( 12.612499)# Expand with one part# 0.000219 0.000003 0.000222 ( 0.000223)# Expand with one part and empty query# 0.000177 0.000011 0.000188 ( 0.000188)### When using the variable path the following regexp is generated# EXPANSION_REGEXP_WITH_VARIABLE_PATH = /(?-mix:^(?:|(?<scheme>(?:[a-zA-Z0-9\-\.\_\~]|%[a-fA-F0-9][a-fA-F0-9])*?)?):\/\/(?:|(?<host>(?:[a-zA-Z0-9\-\.\_\~]|%[a-fA-F0-9][a-fA-F0-9])*?)?)(?:|\/(?<path>(?:[a-zA-Z0-9\-\.\_\~]|%[a-fA-F0-9][a-fA-F0-9])*?(?:\/?(?:[a-zA-Z0-9\-\.\_\~]|%[a-fA-F0-9][a-fA-F0-9])*?)*)?)(?:|\?(?<query>(?:[a-zA-Z0-9\-\.\_\~]|%[a-fA-F0-9][a-fA-F0-9])*=(?:[a-zA-Z0-9\-\.\_\~]|%[a-fA-F0-9][a-fA-F0-9])*?(?:&?(?:[a-zA-Z0-9\-\.\_\~]|%[a-fA-F0-9][a-fA-F0-9])*=(?:[a-zA-Z0-9\-\.\_\~]|%[a-fA-F0-9][a-fA-F0-9])*?)*)?)$)/## When we don't use a variable path the following regexp is generated# EXPANSION_REGEXP_WITHOUT_VARIABLE_PATH = /(?-mix:^(?:|(?<scheme>(?:[a-zA-Z0-9\-\.\_\~]|%[a-fA-F0-9][a-fA-F0-9])*?)?):\/\/(?:|(?<host>(?:[a-zA-Z0-9\-\.\_\~]|%[a-fA-F0-9][a-fA-F0-9])*?)?)(?:|\/(?<path>(?:[a-zA-Z0-9\-\.\_\~]|%[a-fA-F0-9][a-fA-F0-9])*?)?)(?:|\?(?<query>(?:[a-zA-Z0-9\-\.\_\~]|%[a-fA-F0-9][a-fA-F0-9])*=(?:[a-zA-Z0-9\-\.\_\~]|%[a-fA-F0-9][a-fA-F0-9])*?(?:&?(?:[a-zA-Z0-9\-\.\_\~]|%[a-fA-F0-9][a-fA-F0-9])*=(?:[a-zA-Z0-9\-\.\_\~]|%[a-fA-F0-9][a-fA-F0-9])*?)*)?)$)/puts"\n\n\n\n"EXPANSION_REGEXP_WITH_VARIABLE_PATH=/(?-mix:^(?:|(?<scheme>(?:[a-zA-Z0-9\-\.\_\~]|%[a-fA-F0-9][a-fA-F0-9])*?)?):\/\/(?:|(?<host>(?:[a-zA-Z0-9\-\.\_\~]|%[a-fA-F0-9][a-fA-F0-9])*?)?)(?:|\/(?<path>(?:[a-zA-Z0-9\-\.\_\~]|%[a-fA-F0-9][a-fA-F0-9])*?(?:\/?(?:[a-zA-Z0-9\-\.\_\~]|%[a-fA-F0-9][a-fA-F0-9])*?)*)?)(?:|\?(?<query>(?:[a-zA-Z0-9\-\.\_\~]|%[a-fA-F0-9][a-fA-F0-9])*=(?:[a-zA-Z0-9\-\.\_\~]|%[a-fA-F0-9][a-fA-F0-9])*?(?:&?(?:[a-zA-Z0-9\-\.\_\~]|%[a-fA-F0-9][a-fA-F0-9])*=(?:[a-zA-Z0-9\-\.\_\~]|%[a-fA-F0-9][a-fA-F0-9])*?)*)?)$)/EXPANSION_REGEXP_WITHOUT_VARIABLE_PATH=/(?-mix:^(?:|(?<scheme>(?:[a-zA-Z0-9\-\.\_\~]|%[a-fA-F0-9][a-fA-F0-9])*?)?):\/\/(?:|(?<host>(?:[a-zA-Z0-9\-\.\_\~]|%[a-fA-F0-9][a-fA-F0-9])*?)?)(?:|\/(?<path>(?:[a-zA-Z0-9\-\.\_\~]|%[a-fA-F0-9][a-fA-F0-9])*?)?)(?:|\?(?<query>(?:[a-zA-Z0-9\-\.\_\~]|%[a-fA-F0-9][a-fA-F0-9])*=(?:[a-zA-Z0-9\-\.\_\~]|%[a-fA-F0-9][a-fA-F0-9])*?(?:&?(?:[a-zA-Z0-9\-\.\_\~]|%[a-fA-F0-9][a-fA-F0-9])*=(?:[a-zA-Z0-9\-\.\_\~]|%[a-fA-F0-9][a-fA-F0-9])*?)*)?)$)/puts"bad regexp"putsBenchmark.measure{puts"http://host.com/abcdefghijklmnopqrstuvwxyz?a".match(EXPANSION_REGEXP_WITH_VARIABLE_PATH)}puts"\n"puts"better regexp"putsBenchmark.measure{puts"http://host.com/abcdefghijklmnopqrstuvwxyz?a".match(EXPANSION_REGEXP_WITHOUT_VARIABLE_PATH)}# bad regexp## 12.920860 0.016003 12.936863 ( 12.948862)## better regexp## 0.000008 0.000002 0.000010 ( 0.000010)
Thank you @joshkrueger for helping isolate the issue.
The text was updated successfully, but these errors were encountered:
ebenoist
changed the title
ReDDOS when using a variable path and query during extract
ReDOS when using a variable path and query during extract
Nov 2, 2019
I noticed the same thing when extracting a URL with a fragment (#) part where the Template does not include a fragment. Execution time seems to grow exponentially as the path length increases.
EDIT: I saw in CHANGELOG.md that version 2.8.0 fixes a ReDoS vulnerability. However, this issue still occurs on 2.8.0.
Yup, saw the CVE reported today and figured it was this issue. We've removed the need to pass untrusted URIs to this library, but I can also confirm that the issue still exists.
Template Used
When the template above extracts a url with an empty query string, the regex used displays immense performance degradation.
All the time spent (~12 seconds on a modern Macbook Pro) is spent attempting to match the uri with the generated expansion regex.
Bad Regex
Reproduction
Thank you @joshkrueger for helping isolate the issue.
The text was updated successfully, but these errors were encountered: