From 547e809a9699ea48f7135b35857008ef4f37838a Mon Sep 17 00:00:00 2001 From: Keith Gable Date: Sat, 9 Oct 2021 04:08:29 -0700 Subject: [PATCH 01/23] Don't ary.inspect in the lint assertions (backport) (#1765) --- lib/rack/lint.rb | 4 ++-- test/spec_lint.rb | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/rack/lint.rb b/lib/rack/lint.rb index 16b5feea2..67493af45 100644 --- a/lib/rack/lint.rb +++ b/lib/rack/lint.rb @@ -48,10 +48,10 @@ def _call(env) ## and returns an Array of exactly three values: ary = @app.call(env) - assert("response #{ary.inspect} is not an Array , but #{ary.class}") { + assert("response is not an Array, but #{ary.class}") { ary.kind_of? Array } - assert("response array #{ary.inspect} has #{ary.size} elements instead of 3") { + assert("response array has #{ary.size} elements instead of 3") { ary.size == 3 } diff --git a/test/spec_lint.rb b/test/spec_lint.rb index 7b2151b02..f7da81c33 100644 --- a/test/spec_lint.rb +++ b/test/spec_lint.rb @@ -156,7 +156,7 @@ def obj.error(*) end [] }).call(env("rack.multipart.tempfile_factory" => lambda { |filename, content_type| String.new })) }.must_raise(Rack::Lint::LintError). - message.must_equal "response array [] has 0 elements instead of 3" + message.must_equal "response array has 0 elements instead of 3" lambda { Rack::Lint.new(nil).call(env("REQUEST_METHOD" => "FUCKUP?")) @@ -250,14 +250,14 @@ def result.name "" }).call(env({})) }.must_raise(Rack::Lint::LintError). - message.must_include('response "" is not an Array , but String') + message.must_include('response is not an Array, but String') lambda { Rack::Lint.new(lambda { |env| [nil, nil, nil, nil] }).call(env({})) }.must_raise(Rack::Lint::LintError). - message.must_include('response array [nil, nil, nil, nil] has 4 elements instead of 3') + message.must_include('response array has 4 elements instead of 3') end it "notice status errors" do From a2091fa40dc85b98e4de7626eb3d9891c693fbb9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Josef=20=C5=A0im=C3=A1nek?= Date: Sun, 3 Apr 2022 15:54:05 +0200 Subject: [PATCH 02/23] Use custom exception on params too deep error. - backport of df23f57407426a85245e35d8c0c7c90d53dcb74a --- lib/rack/query_parser.rb | 8 ++++++-- test/spec_multipart.rb | 4 ++-- test/spec_request.rb | 10 +++++----- test/spec_utils.rb | 2 +- 4 files changed, 14 insertions(+), 10 deletions(-) diff --git a/lib/rack/query_parser.rb b/lib/rack/query_parser.rb index dbbb18e5a..1c3923c32 100644 --- a/lib/rack/query_parser.rb +++ b/lib/rack/query_parser.rb @@ -16,6 +16,10 @@ class ParameterTypeError < TypeError; end # sequence. class InvalidParameterError < ArgumentError; end + # ParamsTooDeepError is the error that is raised when params are recursively + # nested over the specified limit. + class ParamsTooDeepError < RangeError; end + def self.make_default(key_space_limit, param_depth_limit) new Params, key_space_limit, param_depth_limit end @@ -81,7 +85,7 @@ def parse_nested_query(qs, d = nil) # the structural types represented by two different parameter names are in # conflict, a ParameterTypeError is raised. def normalize_params(params, name, v, depth) - raise RangeError if depth <= 0 + raise ParamsTooDeepError if depth <= 0 name =~ %r(\A[\[\]]*([^\[\]]+)\]*) k = $1 || '' @@ -168,7 +172,7 @@ def [](key) def []=(key, value) @size += key.size if key && !@params.key?(key) - raise RangeError, 'exceeded available parameter key space' if @size > @limit + raise ParamsTooDeepError, 'exceeded available parameter key space' if @size > @limit @params[key] = value end diff --git a/test/spec_multipart.rb b/test/spec_multipart.rb index 717e0dc81..074827e67 100644 --- a/test/spec_multipart.rb +++ b/test/spec_multipart.rb @@ -92,12 +92,12 @@ def multipart_file(name) params['user_sid'].encoding.must_equal Encoding::UTF_8 end - it "raise RangeError if the key space is exhausted" do + it "raise ParamsTooDeepError if the key space is exhausted" do env = Rack::MockRequest.env_for("/", multipart_fixture(:content_type_and_no_filename)) old, Rack::Utils.key_space_limit = Rack::Utils.key_space_limit, 1 begin - lambda { Rack::Multipart.parse_multipart(env) }.must_raise(RangeError) + lambda { Rack::Multipart.parse_multipart(env) }.must_raise(Rack::QueryParser::ParamsTooDeepError) ensure Rack::Utils.key_space_limit = old end diff --git a/test/spec_request.rb b/test/spec_request.rb index 01ddfea19..c8c439b53 100644 --- a/test/spec_request.rb +++ b/test/spec_request.rb @@ -292,7 +292,7 @@ def initialize(*) old, Rack::Utils.key_space_limit = Rack::Utils.key_space_limit, 1 begin req = make_request(env) - lambda { req.GET }.must_raise RangeError + lambda { req.GET }.must_raise Rack::QueryParser::ParamsTooDeepError ensure Rack::Utils.key_space_limit = old end @@ -306,7 +306,7 @@ def initialize(*) begin exp = { "foo" => { "bar" => { "baz" => { "qux" => "1" } } } } make_request(nested_query).GET.must_equal exp - lambda { make_request(plain_query).GET }.must_raise RangeError + lambda { make_request(plain_query).GET }.must_raise Rack::QueryParser::ParamsTooDeepError ensure Rack::Utils.key_space_limit = old end @@ -315,7 +315,7 @@ def initialize(*) it "limit the allowed parameter depth when parsing parameters" do env = Rack::MockRequest.env_for("/?a#{'[a]' * 110}=b") req = make_request(env) - lambda { req.GET }.must_raise RangeError + lambda { req.GET }.must_raise Rack::QueryParser::ParamsTooDeepError env = Rack::MockRequest.env_for("/?a#{'[a]' * 90}=b") req = make_request(env) @@ -331,7 +331,7 @@ def initialize(*) env = Rack::MockRequest.env_for("/?a[a][a][a]=b") req = make_request(env) - lambda { make_request(env).GET }.must_raise RangeError + lambda { make_request(env).GET }.must_raise Rack::QueryParser::ParamsTooDeepError ensure Rack::Utils.param_depth_limit = old end @@ -416,7 +416,7 @@ def initialize(*) old, Rack::Utils.key_space_limit = Rack::Utils.key_space_limit, 1 begin req = make_request(env) - lambda { req.POST }.must_raise RangeError + lambda { req.POST }.must_raise Rack::QueryParser::ParamsTooDeepError ensure Rack::Utils.key_space_limit = old end diff --git a/test/spec_utils.rb b/test/spec_utils.rb index 273dc6c1f..a8bb0ce91 100644 --- a/test/spec_utils.rb +++ b/test/spec_utils.rb @@ -128,7 +128,7 @@ def assert_nested_query(exp, act) lambda { Rack::Utils.parse_nested_query("foo#{"[a]" * len}=bar") - }.must_raise(RangeError) + }.must_raise(Rack::QueryParser::ParamsTooDeepError) Rack::Utils.parse_nested_query("foo#{"[a]" * (len - 1)}=bar") end From 43b5565a73817d66b6d96de2e28d525a2a56f852 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Josef=20=C5=A0im=C3=A1nek?= Date: Mon, 4 Apr 2022 23:17:19 +0200 Subject: [PATCH 03/23] Newer rubies spec compatibility. --- Gemfile | 5 +++++ lib/rack/utils.rb | 7 +++++-- test/spec_mock.rb | 34 +++++++++++++++++----------------- test/testrequest.rb | 4 ++-- 4 files changed, 29 insertions(+), 21 deletions(-) diff --git a/Gemfile b/Gemfile index dc075a4ca..5768eedf7 100644 --- a/Gemfile +++ b/Gemfile @@ -14,6 +14,11 @@ end gem "rubocop", require: false +group :test do + gem "webrick" # gemified in Ruby 3.1+ + gem "psych", "~> 4.0" # using YAML#unsafe_load in tests which is 4.0+ only +end + # Alternative solution that might work, but it has bad interactions with # Gemfile.lock if that gets committed/reused: # c_platforms = [:mri] if Gem.platforms.last.os == "java" diff --git a/lib/rack/utils.rb b/lib/rack/utils.rb index d3b3b1d42..34849ded1 100644 --- a/lib/rack/utils.rb +++ b/lib/rack/utils.rb @@ -22,6 +22,9 @@ module Utils COMMON_SEP = QueryParser::COMMON_SEP KeySpaceConstrainedParams = QueryParser::Params + RFC2822_DAY_NAME = [ 'Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat' ] + RFC2822_MONTH_NAME = [ 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec' ] + class << self attr_accessor :default_query_parser end @@ -327,8 +330,8 @@ def rfc2822(time) # weekday and month. # def rfc2109(time) - wday = Time::RFC2822_DAY_NAME[time.wday] - mon = Time::RFC2822_MONTH_NAME[time.mon - 1] + wday = RFC2822_DAY_NAME[time.wday] + mon = RFC2822_MONTH_NAME[time.mon - 1] time.strftime("#{wday}, %d-#{mon}-%Y %H:%M:%S GMT") end diff --git a/test/spec_mock.rb b/test/spec_mock.rb index d2311f5a2..73fb7b235 100644 --- a/test/spec_mock.rb +++ b/test/spec_mock.rb @@ -47,7 +47,7 @@ it "provide sensible defaults" do res = Rack::MockRequest.new(app).request - env = YAML.load(res.body) + env = YAML.unsafe_load(res.body) env["REQUEST_METHOD"].must_equal "GET" env["SERVER_NAME"].must_equal "example.org" env["SERVER_PORT"].must_equal "80" @@ -60,23 +60,23 @@ it "allow GET/POST/PUT/DELETE/HEAD" do res = Rack::MockRequest.new(app).get("", input: "foo") - env = YAML.load(res.body) + env = YAML.unsafe_load(res.body) env["REQUEST_METHOD"].must_equal "GET" res = Rack::MockRequest.new(app).post("", input: "foo") - env = YAML.load(res.body) + env = YAML.unsafe_load(res.body) env["REQUEST_METHOD"].must_equal "POST" res = Rack::MockRequest.new(app).put("", input: "foo") - env = YAML.load(res.body) + env = YAML.unsafe_load(res.body) env["REQUEST_METHOD"].must_equal "PUT" res = Rack::MockRequest.new(app).patch("", input: "foo") - env = YAML.load(res.body) + env = YAML.unsafe_load(res.body) env["REQUEST_METHOD"].must_equal "PATCH" res = Rack::MockRequest.new(app).delete("", input: "foo") - env = YAML.load(res.body) + env = YAML.unsafe_load(res.body) env["REQUEST_METHOD"].must_equal "DELETE" Rack::MockRequest.env_for("/", method: "HEAD")["REQUEST_METHOD"] @@ -102,11 +102,11 @@ it "allow posting" do res = Rack::MockRequest.new(app).get("", input: "foo") - env = YAML.load(res.body) + env = YAML.unsafe_load(res.body) env["mock.postdata"].must_equal "foo" res = Rack::MockRequest.new(app).post("", input: StringIO.new("foo")) - env = YAML.load(res.body) + env = YAML.unsafe_load(res.body) env["mock.postdata"].must_equal "foo" end @@ -115,7 +115,7 @@ get("https://bla.example.org:9292/meh/foo?bar") res.must_be_kind_of Rack::MockResponse - env = YAML.load(res.body) + env = YAML.unsafe_load(res.body) env["REQUEST_METHOD"].must_equal "GET" env["SERVER_NAME"].must_equal "bla.example.org" env["SERVER_PORT"].must_equal "9292" @@ -129,7 +129,7 @@ get("https://example.org/foo") res.must_be_kind_of Rack::MockResponse - env = YAML.load(res.body) + env = YAML.unsafe_load(res.body) env["REQUEST_METHOD"].must_equal "GET" env["SERVER_NAME"].must_equal "example.org" env["SERVER_PORT"].must_equal "443" @@ -144,7 +144,7 @@ get("foo") res.must_be_kind_of Rack::MockResponse - env = YAML.load(res.body) + env = YAML.unsafe_load(res.body) env["REQUEST_METHOD"].must_equal "GET" env["SERVER_NAME"].must_equal "example.org" env["SERVER_PORT"].must_equal "80" @@ -155,13 +155,13 @@ it "properly convert method name to an uppercase string" do res = Rack::MockRequest.new(app).request(:get) - env = YAML.load(res.body) + env = YAML.unsafe_load(res.body) env["REQUEST_METHOD"].must_equal "GET" end it "accept params and build query string for GET requests" do res = Rack::MockRequest.new(app).get("/foo?baz=2", params: { foo: { bar: "1" } }) - env = YAML.load(res.body) + env = YAML.unsafe_load(res.body) env["REQUEST_METHOD"].must_equal "GET" env["QUERY_STRING"].must_include "baz=2" env["QUERY_STRING"].must_include "foo[bar]=1" @@ -171,7 +171,7 @@ it "accept raw input in params for GET requests" do res = Rack::MockRequest.new(app).get("/foo?baz=2", params: "foo[bar]=1") - env = YAML.load(res.body) + env = YAML.unsafe_load(res.body) env["REQUEST_METHOD"].must_equal "GET" env["QUERY_STRING"].must_include "baz=2" env["QUERY_STRING"].must_include "foo[bar]=1" @@ -181,7 +181,7 @@ it "accept params and build url encoded params for POST requests" do res = Rack::MockRequest.new(app).post("/foo", params: { foo: { bar: "1" } }) - env = YAML.load(res.body) + env = YAML.unsafe_load(res.body) env["REQUEST_METHOD"].must_equal "POST" env["QUERY_STRING"].must_equal "" env["PATH_INFO"].must_equal "/foo" @@ -191,7 +191,7 @@ it "accept raw input in params for POST requests" do res = Rack::MockRequest.new(app).post("/foo", params: "foo[bar]=1") - env = YAML.load(res.body) + env = YAML.unsafe_load(res.body) env["REQUEST_METHOD"].must_equal "POST" env["QUERY_STRING"].must_equal "" env["PATH_INFO"].must_equal "/foo" @@ -202,7 +202,7 @@ it "accept params and build multipart encoded params for POST requests" do files = Rack::Multipart::UploadedFile.new(File.join(File.dirname(__FILE__), "multipart", "file1.txt")) res = Rack::MockRequest.new(app).post("/foo", params: { "submit-name" => "Larry", "files" => files }) - env = YAML.load(res.body) + env = YAML.unsafe_load(res.body) env["REQUEST_METHOD"].must_equal "POST" env["QUERY_STRING"].must_equal "" env["PATH_INFO"].must_equal "/foo" diff --git a/test/testrequest.rb b/test/testrequest.rb index aabe7fa6b..481a4e54d 100644 --- a/test/testrequest.rb +++ b/test/testrequest.rb @@ -42,7 +42,7 @@ def GET(path, header = {}) http.request(get) { |response| @status = response.code.to_i begin - @response = YAML.load(response.body) + @response = YAML.unsafe_load(response.body) rescue TypeError, ArgumentError @response = nil end @@ -60,7 +60,7 @@ def POST(path, formdata = {}, header = {}) post.basic_auth user, passwd if user && passwd http.request(post) { |response| @status = response.code.to_i - @response = YAML.load(response.body) + @response = YAML.unsafe_load(response.body) } } end From 031b47a8d5bd7b7b2416b751f522acfd7a5af036 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Josef=20=C5=A0im=C3=A1nek?= Date: Mon, 4 Apr 2022 23:17:34 +0200 Subject: [PATCH 04/23] Replace CircleCI with GitHub Actions. --- .circleci/config.yml | 96 ------------------------------- .github/workflows/development.yml | 36 ++++++++++++ 2 files changed, 36 insertions(+), 96 deletions(-) delete mode 100644 .circleci/config.yml create mode 100644 .github/workflows/development.yml diff --git a/.circleci/config.yml b/.circleci/config.yml deleted file mode 100644 index 0873697fd..000000000 --- a/.circleci/config.yml +++ /dev/null @@ -1,96 +0,0 @@ -workflows: - version: 2 - - test: - jobs: - - test-jruby - - test-ruby-2.3 - - test-ruby-2.4 - - test-ruby-2.5 - - test-ruby-2.6 - - test-ruby-2.7 - -version: 2 - -default-steps: &default-steps - - checkout - - run: sudo apt-get install lighttpd libfcgi-dev libmemcached-dev - - # Restore bundle cache - - type: cache-restore - key: rack-{{ checksum "rack.gemspec" }}-{{ checksum "Gemfile" }} - - # Bundle install dependencies - - run: bundle install --path vendor/bundle - - # Store bundle cache - - type: cache-save - key: rack-{{ checksum "rack.gemspec" }}-{{ checksum "Gemfile" }} - paths: - - vendor/bundle - - - run: bundle exec rubocop - - - run: bundle exec rake ci - -jobs: - test-ruby-2.3: - docker: - - image: circleci/ruby:2.3 - # Spawn a process owned by root - # This works around an issue explained here: - # https://github.com/circleci/circleci-images/pull/132 - command: sudo /bin/sh - - image: memcached:1.4 - steps: *default-steps - - test-ruby-2.4: - docker: - - image: circleci/ruby:2.4 - # Spawn a process owned by root - # This works around an issue explained here: - # https://github.com/circleci/circleci-images/pull/132 - command: sudo /bin/sh - - image: memcached:1.4 - steps: *default-steps - - test-ruby-2.5: - docker: - - image: circleci/ruby:2.5 - # Spawn a process owned by root - # This works around an issue explained here: - # https://github.com/circleci/circleci-images/pull/132 - command: sudo /bin/sh - - image: memcached:1.4 - steps: *default-steps - - test-ruby-2.6: - docker: - - image: circleci/ruby:2.6 - # Spawn a process owned by root - # This works around an issue explained here: - # https://github.com/circleci/circleci-images/pull/132 - command: sudo /bin/sh - - image: memcached:1.4 - steps: *default-steps - - test-ruby-2.7: - docker: - - image: circleci/ruby:2.7 - # Spawn a process owned by root - # This works around an issue explained here: - # https://github.com/circleci/circleci-images/pull/132 - command: sudo /bin/sh - - image: memcached:1.4 - steps: *default-steps - - test-jruby: - docker: - - image: circleci/jruby - # Spawn a process owned by root - # This works around an issue explained here: - # https://github.com/circleci/circleci-images/pull/132 - command: sudo /bin/sh - - image: memcached:1.4 - steps: *default-steps - diff --git a/.github/workflows/development.yml b/.github/workflows/development.yml new file mode 100644 index 000000000..31719d2e9 --- /dev/null +++ b/.github/workflows/development.yml @@ -0,0 +1,36 @@ +name: Development + +on: [push, pull_request] + +jobs: + test: + strategy: + fail-fast: false + matrix: + os: [ubuntu-20.04] + ruby: [2.4, 2.5, 2.6, 2.7, '3.0', 3.1] + runs-on: ${{matrix.os}} + steps: + - uses: actions/checkout@v2 + + - uses: ruby/setup-ruby@v1 + with: + ruby-version: ${{matrix.ruby}} + + - uses: actions/cache@v1 + with: + path: vendor/bundle + key: bundle-use-ruby-${{matrix.os}}-${{matrix.ruby}}-${{hashFiles('**/Gemfile')}} + restore-keys: | + bundle-use-ruby-${{matrix.os}}-${{matrix.ruby}}- + + - name: Installing packages + run: sudo apt-get install libfcgi-dev libmemcached-dev + + - name: Bundle install... + run: | + gem update --system + bundle config path vendor/bundle + bundle install + + - run: bundle exec rake From 991e2a5083f1e2edf7e8b05bf026a17673bb27e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Josef=20=C5=A0im=C3=A1nek?= Date: Sun, 10 Apr 2022 23:06:40 +0200 Subject: [PATCH 05/23] Add Ruby 2.3 compatibility for tests, add Ruby 2.3 to CI. (#1863) --- .github/workflows/development.yml | 2 +- Gemfile | 2 +- test/psych_fix.rb | 8 ++++++++ test/spec_mock.rb | 1 + test/testrequest.rb | 1 + 5 files changed, 12 insertions(+), 2 deletions(-) create mode 100644 test/psych_fix.rb diff --git a/.github/workflows/development.yml b/.github/workflows/development.yml index 31719d2e9..081acffd6 100644 --- a/.github/workflows/development.yml +++ b/.github/workflows/development.yml @@ -8,7 +8,7 @@ jobs: fail-fast: false matrix: os: [ubuntu-20.04] - ruby: [2.4, 2.5, 2.6, 2.7, '3.0', 3.1] + ruby: [2.3, 2.4, 2.5, 2.6, 2.7, '3.0', 3.1] runs-on: ${{matrix.os}} steps: - uses: actions/checkout@v2 diff --git a/Gemfile b/Gemfile index 5768eedf7..b6ce15e4b 100644 --- a/Gemfile +++ b/Gemfile @@ -16,7 +16,7 @@ gem "rubocop", require: false group :test do gem "webrick" # gemified in Ruby 3.1+ - gem "psych", "~> 4.0" # using YAML#unsafe_load in tests which is 4.0+ only + gem "psych" end # Alternative solution that might work, but it has bad interactions with diff --git a/test/psych_fix.rb b/test/psych_fix.rb new file mode 100644 index 000000000..ef8a5be3c --- /dev/null +++ b/test/psych_fix.rb @@ -0,0 +1,8 @@ +# Work correctly with older versions of Psych, having +# unsafe_load call load (in older versions, load operates +# as unsafe_load in current version). +unless YAML.respond_to?(:unsafe_load) + def YAML.unsafe_load(body) + load(body) + end +end diff --git a/test/spec_mock.rb b/test/spec_mock.rb index 73fb7b235..c31aa5535 100644 --- a/test/spec_mock.rb +++ b/test/spec_mock.rb @@ -2,6 +2,7 @@ require_relative 'helper' require 'yaml' +require_relative 'psych_fix' app = Rack::Lint.new(lambda { |env| req = Rack::Request.new(env) diff --git a/test/testrequest.rb b/test/testrequest.rb index 481a4e54d..b85aae831 100644 --- a/test/testrequest.rb +++ b/test/testrequest.rb @@ -1,6 +1,7 @@ # frozen_string_literal: true require 'yaml' +require_relative 'psych_fix' require 'net/http' require 'rack/lint' From e71d214f8573625b6f832cb9edd2aac898446e53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Josef=20=C5=A0im=C3=A1nek?= Date: Mon, 11 Apr 2022 23:44:43 +0200 Subject: [PATCH 06/23] Ensure Rack::QueryParser::ParamsTooDeepError is inherited from RangeError. (#1864) --- test/spec_utils.rb | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/test/spec_utils.rb b/test/spec_utils.rb index a8bb0ce91..90676258f 100644 --- a/test/spec_utils.rb +++ b/test/spec_utils.rb @@ -133,6 +133,12 @@ def assert_nested_query(exp, act) Rack::Utils.parse_nested_query("foo#{"[a]" * (len - 1)}=bar") end + # ParamsTooDeepError was introduced in the middle of 2.2 releases + # and this test is here to ensure backwards compatibility + it "ParamsTooDeepError is inherited from originally used RangeError" do + (Rack::QueryParser::ParamsTooDeepError < RangeError).must_equal(true) + end + it "parse nested query strings correctly" do Rack::Utils.parse_nested_query("foo"). must_equal "foo" => nil From e7c5b944938e66106fab9d4b4b1a3097ebac55af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Josef=20=C5=A0im=C3=A1nek?= Date: Sat, 25 Jun 2022 06:51:31 +0200 Subject: [PATCH 07/23] Expect additional optional version segment in version test. (#1913) --- test/spec_server.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/spec_server.rb b/test/spec_server.rb index 20992a0f9..34912b4d8 100644 --- a/test/spec_server.rb +++ b/test/spec_server.rb @@ -162,7 +162,7 @@ def cgi.valid_options; { "FOO=BAR" => "BAZ" } end end it "support -v option to get version" do - test_options_server('-v').must_match(/\ARack \d\.\d \(Release: \d+\.\d+\.\d+\)\nexited\z/) + test_options_server('-v').must_match(/\ARack \d\.\d \(Release: \d+\.\d+\.\d+(\.\d+)?\)\nexited\z/) end it "warn for invalid --profile-mode option" do From d62c290f5827efd595f704bfa36c03adc1f36522 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Josef=20=C5=A0im=C3=A1nek?= Date: Sat, 25 Jun 2022 12:06:09 +0200 Subject: [PATCH 08/23] Add 'custom exception on params too deep error' change to CHANGELOG. (#1914) - format latest versions changes in consistent way --- CHANGELOG.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 80f57c99f..595f33190 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,11 +4,19 @@ All notable changes to this project will be documented in this file. For info on ## [2.2.3.1] - 2022-05-27 +### Security + - [CVE-2022-30123] Fix shell escaping issue in Common Logger - [CVE-2022-30122] Restrict parsing of broken MIME attachments +### Changed + +- Use custom exception on params too deep error. ([#1838](https://github.com/rack/rack/pull/1838), [@simi](https://github.com/simi)) + ## [2.2.3] - 2020-02-11 +### Security + - [CVE-2020-8184] Only decode cookie values ## [2.2.2] - 2020-02-11 From 0077900695ffe6e30d8fa036c35a10feeefb77e3 Mon Sep 17 00:00:00 2001 From: Samuel Williams Date: Fri, 1 Jul 2022 10:03:22 +1200 Subject: [PATCH 09/23] Better handling of case-insensitive headers for `Rack::Etag` middleware. (#1919) --- CHANGELOG.md | 4 ++++ lib/rack/etag.rb | 2 ++ test/spec_etag.rb | 6 ++++++ 3 files changed, 12 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 595f33190..189031810 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ All notable changes to this project will be documented in this file. For info on how to format all future additions to this file please reference [Keep A Changelog](https://keepachangelog.com/en/1.0.0/). +## [2.2.4] + +- Better support for lower case headers in `Rack::ETag` middleware. ([#1919](https://github.com/rack/rack/pull/1919), [@ioquatix](https://github.com/ioquatix)) + ## [2.2.3.1] - 2022-05-27 ### Security diff --git a/lib/rack/etag.rb b/lib/rack/etag.rb index aceb449dd..5039437e1 100644 --- a/lib/rack/etag.rb +++ b/lib/rack/etag.rb @@ -26,6 +26,8 @@ def initialize(app, no_cache_control = nil, cache_control = DEFAULT_CACHE_CONTRO def call(env) status, headers, body = @app.call(env) + headers = Utils::HeaderHash[headers] + if etag_status?(status) && etag_body?(body) && !skip_caching?(headers) original_body = body digest, new_body = digest_body(body) diff --git a/test/spec_etag.rb b/test/spec_etag.rb index 311ad8033..77c2dfc32 100644 --- a/test/spec_etag.rb +++ b/test/spec_etag.rb @@ -48,6 +48,12 @@ def res.to_path ; "/tmp/hello.txt" ; end response[1]['Cache-Control'].must_equal 'no-cache' end + it "does not set a cache-control if it is already set" do + app = lambda { |env| [201, { 'Content-Type' => 'text/plain', 'cache-control' => 'public' }, ["Hello, World!"]] } + response = etag(app).call(request) + response[1]['cache-control'].must_equal 'public' + end + it "not set Cache-Control if it is already set" do app = lambda { |env| [201, { 'Content-Type' => 'text/plain', 'Cache-Control' => 'public' }, ["Hello, World!"]] } response = etag(app).call(request) From 0ae9ff26828aaecfe91fdd6628198f02ff9a0bdb Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Thu, 30 Jun 2022 15:16:17 -0700 Subject: [PATCH 10/23] bump version --- lib/rack/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rack/version.rb b/lib/rack/version.rb index 233e16223..f05a2c602 100644 --- a/lib/rack/version.rb +++ b/lib/rack/version.rb @@ -20,7 +20,7 @@ def self.version VERSION.join(".") end - RELEASE = "2.2.3.1" + RELEASE = "2.2.4" # Return the Rack release as a dotted string. def self.release From abca7d59c566320f1b60d1f5224beac9d201fa3b Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Thu, 30 Jun 2022 15:18:29 -0700 Subject: [PATCH 11/23] fixup changelog --- CHANGELOG.md | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 189031810..a07dae6e3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,9 +2,10 @@ All notable changes to this project will be documented in this file. For info on how to format all future additions to this file please reference [Keep A Changelog](https://keepachangelog.com/en/1.0.0/). -## [2.2.4] +## [2.2.4] - 2022-06-30 - Better support for lower case headers in `Rack::ETag` middleware. ([#1919](https://github.com/rack/rack/pull/1919), [@ioquatix](https://github.com/ioquatix)) +- Use custom exception on params too deep error. ([#1838](https://github.com/rack/rack/pull/1838), [@simi](https://github.com/simi)) ## [2.2.3.1] - 2022-05-27 @@ -13,10 +14,6 @@ All notable changes to this project will be documented in this file. For info on - [CVE-2022-30123] Fix shell escaping issue in Common Logger - [CVE-2022-30122] Restrict parsing of broken MIME attachments -### Changed - -- Use custom exception on params too deep error. ([#1838](https://github.com/rack/rack/pull/1838), [@simi](https://github.com/simi)) - ## [2.2.3] - 2020-02-11 ### Security From ac892ab84dbac20729fcc96da8fcdb5eb4aa7bb9 Mon Sep 17 00:00:00 2001 From: Wei Zhe Date: Mon, 26 Dec 2022 19:48:01 +0800 Subject: [PATCH 12/23] Fix Regexp deprecated third argument with Regexp::NOENCODING (#1998) --- lib/rack/urlmap.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rack/urlmap.rb b/lib/rack/urlmap.rb index 31a642c41..8462f9206 100644 --- a/lib/rack/urlmap.rb +++ b/lib/rack/urlmap.rb @@ -35,7 +35,7 @@ def remap(map) end location = location.chomp('/') - match = Regexp.new("^#{Regexp.quote(location).gsub('/', '/+')}(.*)", nil, 'n') + match = Regexp.new("^#{Regexp.quote(location).gsub('/', '/+')}(.*)", Regexp::NOENCODING) [host, location, match, app] }.sort_by do |(host, location, _, _)| From 7bb58c83a7ee1ef40233f57d07275a130fbcc20e Mon Sep 17 00:00:00 2001 From: Samuel Williams Date: Tue, 27 Dec 2022 09:03:06 +1300 Subject: [PATCH 13/23] Bump patch release. --- CHANGELOG.md | 6 ++++++ lib/rack/version.rb | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a07dae6e3..3d988e8d1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,12 @@ All notable changes to this project will be documented in this file. For info on how to format all future additions to this file please reference [Keep A Changelog](https://keepachangelog.com/en/1.0.0/). +## [2.2.5] - 2022-12-27 + +### Fixed + +- `Rack::URLMap` uses non-deprecated form of `Regexp.new`. ([#1998](https://github.com/rack/rack/pull/1998), [@weizheheng](https://github.com/weizheheng)) + ## [2.2.4] - 2022-06-30 - Better support for lower case headers in `Rack::ETag` middleware. ([#1919](https://github.com/rack/rack/pull/1919), [@ioquatix](https://github.com/ioquatix)) diff --git a/lib/rack/version.rb b/lib/rack/version.rb index f05a2c602..f8324c46f 100644 --- a/lib/rack/version.rb +++ b/lib/rack/version.rb @@ -20,7 +20,7 @@ def self.version VERSION.join(".") end - RELEASE = "2.2.4" + RELEASE = "2.2.5" # Return the Rack release as a dotted string. def self.release From 2a82c88f1a468d8f68d9d7640886f5153142c272 Mon Sep 17 00:00:00 2001 From: Samuel Williams Date: Tue, 27 Dec 2022 09:07:27 +1300 Subject: [PATCH 14/23] Update tests to work on latest Rubies. (#1999) * Don't update to latest rubygems. * Test on Ruby v3.2. --- .github/workflows/development.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/development.yml b/.github/workflows/development.yml index 081acffd6..0757016be 100644 --- a/.github/workflows/development.yml +++ b/.github/workflows/development.yml @@ -8,7 +8,7 @@ jobs: fail-fast: false matrix: os: [ubuntu-20.04] - ruby: [2.3, 2.4, 2.5, 2.6, 2.7, '3.0', 3.1] + ruby: [2.3, 2.4, 2.5, 2.6, 2.7, '3.0', 3.1, 3.2] runs-on: ${{matrix.os}} steps: - uses: actions/checkout@v2 @@ -29,7 +29,6 @@ jobs: - name: Bundle install... run: | - gem update --system bundle config path vendor/bundle bundle install From 8312a2fd6aee0950d7b2deb548aaf600cb871d80 Mon Sep 17 00:00:00 2001 From: Samuel Williams Date: Tue, 27 Dec 2022 09:11:24 +1300 Subject: [PATCH 15/23] Remove leading dot to fix compatibility with latest cgi gem. (#1988) # Conflicts: # test/spec_mock_response.rb --- test/spec_mock.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/spec_mock.rb b/test/spec_mock.rb index c31aa5535..ed679c3e9 100644 --- a/test/spec_mock.rb +++ b/test/spec_mock.rb @@ -19,8 +19,8 @@ req.GET["status"] || 200, "Content-Type" => "text/yaml" ) - response.set_cookie("session_test", { value: "session_test", domain: ".test.com", path: "/" }) - response.set_cookie("secure_test", { value: "secure_test", domain: ".test.com", path: "/", secure: true }) + response.set_cookie("session_test", { value: "session_test", domain: "test.com", path: "/" }) + response.set_cookie("secure_test", { value: "secure_test", domain: "test.com", path: "/", secure: true }) response.set_cookie("persistent_test", { value: "persistent_test", max_age: 15552000, path: "/" }) response.finish }) @@ -293,7 +293,7 @@ res = Rack::MockRequest.new(app).get("") session_cookie = res.cookie("session_test") session_cookie.value[0].must_equal "session_test" - session_cookie.domain.must_equal ".test.com" + session_cookie.domain.must_equal "test.com" session_cookie.path.must_equal "/" session_cookie.secure.must_equal false session_cookie.expires.must_be_nil @@ -314,7 +314,7 @@ res = Rack::MockRequest.new(app).get("") secure_cookie = res.cookie("secure_test") secure_cookie.value[0].must_equal "secure_test" - secure_cookie.domain.must_equal ".test.com" + secure_cookie.domain.must_equal "test.com" secure_cookie.path.must_equal "/" secure_cookie.secure.must_equal true secure_cookie.expires.must_be_nil From c0f9de4844052b7867180c587d1b6969be2f114d Mon Sep 17 00:00:00 2001 From: Jean byroot Boussier Date: Mon, 16 Jan 2023 21:53:58 +0100 Subject: [PATCH 16/23] Rack::MethodOverride handle QueryParser::ParamsTooDeepError (#2011) This middleware already handle two types of parsing issues but somehow not this one. Co-authored-by: Jean Boussier --- lib/rack/method_override.rb | 2 +- test/spec_method_override.rb | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/lib/rack/method_override.rb b/lib/rack/method_override.rb index 453901fc6..b586f5339 100644 --- a/lib/rack/method_override.rb +++ b/lib/rack/method_override.rb @@ -43,7 +43,7 @@ def allowed_methods def method_override_param(req) req.POST[METHOD_OVERRIDE_PARAM_KEY] - rescue Utils::InvalidParameterError, Utils::ParameterTypeError + rescue Utils::InvalidParameterError, Utils::ParameterTypeError, QueryParser::ParamsTooDeepError req.get_header(RACK_ERRORS).puts "Invalid or incomplete POST params" rescue EOFError req.get_header(RACK_ERRORS).puts "Bad request content body" diff --git a/test/spec_method_override.rb b/test/spec_method_override.rb index 5909907b4..ddb105bdf 100644 --- a/test/spec_method_override.rb +++ b/test/spec_method_override.rb @@ -100,6 +100,13 @@ def app env[Rack::RACK_ERRORS].read.must_match /Bad request content body/ end + it "not modify REQUEST_METHOD for POST requests when the params are unparseable because too deep" do + env = Rack::MockRequest.env_for("/", method: "POST", input: ("[a]" * 36) + "=1") + app.call env + + env["REQUEST_METHOD"].must_equal "POST" + end + it "not modify REQUEST_METHOD for POST requests when the params are unparseable" do env = Rack::MockRequest.env_for("/", method: "POST", input: "(%bad-params%)") app.call env From ea39e49442e0008bfce4ad628ce52a4be2a20b5b Mon Sep 17 00:00:00 2001 From: Samuel Williams Date: Tue, 17 Jan 2023 09:59:19 +1300 Subject: [PATCH 17/23] Bump patch version. --- CHANGELOG.md | 4 ++++ lib/rack/version.rb | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3d988e8d1..8d97e94de 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ All notable changes to this project will be documented in this file. For info on how to format all future additions to this file please reference [Keep A Changelog](https://keepachangelog.com/en/1.0.0/). +## [2.2.6] - 2022-01-17 + +- Extend `Rack::MethodOverride` to handle `QueryParser::ParamsTooDeepError` error. ([#2011](https://github.com/rack/rack/pull/2011), [@byroot](https://github.com/byroot)) + ## [2.2.5] - 2022-12-27 ### Fixed diff --git a/lib/rack/version.rb b/lib/rack/version.rb index f8324c46f..bc41abeab 100644 --- a/lib/rack/version.rb +++ b/lib/rack/version.rb @@ -20,7 +20,7 @@ def self.version VERSION.join(".") end - RELEASE = "2.2.5" + RELEASE = "2.2.6" # Return the Rack release as a dotted string. def self.release From 19e49f0f185d7e42ed5b402baec6c897a8c48029 Mon Sep 17 00:00:00 2001 From: John Hawthorn Date: Wed, 3 Aug 2022 00:19:56 -0700 Subject: [PATCH 18/23] Forbid control characters in attributes This commit restricts the characters accepted in ATTRIBUTE_CHAR, forbidding control characters and fixing a ReDOS vulnerability. This also now should fully follow the RFCs. RFC 2231, Section 7 specifies: attribute-char := RFC 2045, Appendix A specifies: tspecials := "(" / ")" / "<" / ">" / "@" / "," / ";" / ":" / "\" / <"> "/" / "[" / "]" / "?" / "=" RFC 822, Section 3.3 specifies: CTL = ; ( 177, 127.) SPACE = ; ( 40, 32.) [CVE-2022-44572] --- lib/rack/multipart.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rack/multipart.rb b/lib/rack/multipart.rb index 10f8e5fa3..7695fe76b 100644 --- a/lib/rack/multipart.rb +++ b/lib/rack/multipart.rb @@ -21,7 +21,7 @@ module Multipart MULTIPART_CONTENT_DISPOSITION = /Content-Disposition:.*;\s*name=(#{VALUE})/ni MULTIPART_CONTENT_ID = /Content-ID:\s*([^#{EOL}]*)/ni # Updated definitions from RFC 2231 - ATTRIBUTE_CHAR = %r{[^ \t\v\n\r)(><@,;:\\"/\[\]?='*%]} + ATTRIBUTE_CHAR = %r{[^ \x00-\x1f\x7f)(><@,;:\\"/\[\]?='*%]} ATTRIBUTE = /#{ATTRIBUTE_CHAR}+/ SECTION = /\*[0-9]+/ REGULAR_PARAMETER_NAME = /#{ATTRIBUTE}#{SECTION}?/ From ee25ab9a7ee981d7578f559701085b0cf39bde77 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Tue, 17 Jan 2023 12:14:29 -0800 Subject: [PATCH 19/23] Fix ReDoS vulnerability in multipart parser This commit fixes a ReDoS vulnerability when parsing the Content-Disposition field in multipart attachments Thanks to @ooooooo_q for the patch! [CVE-2022-44571] --- lib/rack/multipart.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rack/multipart.rb b/lib/rack/multipart.rb index 7695fe76b..fdae808a8 100644 --- a/lib/rack/multipart.rb +++ b/lib/rack/multipart.rb @@ -18,7 +18,7 @@ module Multipart VALUE = /"(?:\\"|[^"])*"|#{TOKEN}/ BROKEN = /^#{CONDISP}.*;\s*filename=(#{VALUE})/i MULTIPART_CONTENT_TYPE = /Content-Type: (.*)#{EOL}/ni - MULTIPART_CONTENT_DISPOSITION = /Content-Disposition:.*;\s*name=(#{VALUE})/ni + MULTIPART_CONTENT_DISPOSITION = /Content-Disposition:[^:]*;\s*name=(#{VALUE})/ni MULTIPART_CONTENT_ID = /Content-ID:\s*([^#{EOL}]*)/ni # Updated definitions from RFC 2231 ATTRIBUTE_CHAR = %r{[^ \x00-\x1f\x7f)(><@,;:\\"/\[\]?='*%]} From 3677f170b4ac713defb84cd6a86431623ba0adc0 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Tue, 17 Jan 2023 12:36:48 -0800 Subject: [PATCH 20/23] Update changelog --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8d97e94de..90f1d2b8d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,12 @@ All notable changes to this project will be documented in this file. For info on how to format all future additions to this file please reference [Keep A Changelog](https://keepachangelog.com/en/1.0.0/). +## [2.2.6.1] - 2022-01-17 + +- [CVE-2022-44571] Fix ReDoS vulnerability in multipart parser +- [CVE-2022-44570] Fix ReDoS in Rack::Utils.get_byte_ranges +- [CVE-2022-44572] Forbid control characters in attributes (also ReDoS) + ## [2.2.6] - 2022-01-17 - Extend `Rack::MethodOverride` to handle `QueryParser::ParamsTooDeepError` error. ([#2011](https://github.com/rack/rack/pull/2011), [@byroot](https://github.com/byroot)) From 20bc90c2431d7fabcd1873410543cf3d72f65004 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Tue, 17 Jan 2023 12:46:41 -0800 Subject: [PATCH 21/23] bump version --- lib/rack/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rack/version.rb b/lib/rack/version.rb index bc41abeab..07f5bbc83 100644 --- a/lib/rack/version.rb +++ b/lib/rack/version.rb @@ -20,7 +20,7 @@ def self.version VERSION.join(".") end - RELEASE = "2.2.6" + RELEASE = "2.2.6.1" # Return the Rack release as a dotted string. def self.release From f6d4f528f2df1318a6612845db0b59adc7fe8fc1 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Tue, 17 Jan 2023 12:04:37 -0800 Subject: [PATCH 22/23] Fix ReDoS in Rack::Utils.get_byte_ranges This commit fixes a ReDoS problem in `get_byte_ranges`. Thanks @ooooooo_q for the patch! [CVE-2022-44570] --- lib/rack/utils.rb | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/lib/rack/utils.rb b/lib/rack/utils.rb index 34849ded1..14d9e17da 100644 --- a/lib/rack/utils.rb +++ b/lib/rack/utils.rb @@ -348,17 +348,18 @@ def get_byte_ranges(http_range, size) return nil unless http_range && http_range =~ /bytes=([^;]+)/ ranges = [] $1.split(/,\s*/).each do |range_spec| - return nil unless range_spec =~ /(\d*)-(\d*)/ - r0, r1 = $1, $2 - if r0.empty? - return nil if r1.empty? + return nil unless range_spec.include?('-') + range = range_spec.split('-') + r0, r1 = range[0], range[1] + if r0.nil? || r0.empty? + return nil if r1.nil? # suffix-byte-range-spec, represents trailing suffix of file r0 = size - r1.to_i r0 = 0 if r0 < 0 r1 = size - 1 else r0 = r0.to_i - if r1.empty? + if r1.nil? r1 = size - 1 else r1 = r1.to_i From 2606ac5d5d180c00a8cbcaa4d634276bab06500e Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Tue, 17 Jan 2023 13:21:08 -0800 Subject: [PATCH 23/23] bumping version --- CHANGELOG.md | 5 ++++- lib/rack/version.rb | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 90f1d2b8d..ff8a58e00 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,10 +2,13 @@ All notable changes to this project will be documented in this file. For info on how to format all future additions to this file please reference [Keep A Changelog](https://keepachangelog.com/en/1.0.0/). +## [2.2.6.2] - 2022-01-17 + +- [CVE-2022-44570] Fix ReDoS in Rack::Utils.get_byte_ranges + ## [2.2.6.1] - 2022-01-17 - [CVE-2022-44571] Fix ReDoS vulnerability in multipart parser -- [CVE-2022-44570] Fix ReDoS in Rack::Utils.get_byte_ranges - [CVE-2022-44572] Forbid control characters in attributes (also ReDoS) ## [2.2.6] - 2022-01-17 diff --git a/lib/rack/version.rb b/lib/rack/version.rb index 07f5bbc83..d829db1c7 100644 --- a/lib/rack/version.rb +++ b/lib/rack/version.rb @@ -20,7 +20,7 @@ def self.version VERSION.join(".") end - RELEASE = "2.2.6.1" + RELEASE = "2.2.6.2" # Return the Rack release as a dotted string. def self.release