diff --git a/trunk/PKGBUILD b/trunk/PKGBUILD index 473cb11..d62e12d 100644 --- a/trunk/PKGBUILD +++ b/trunk/PKGBUILD @@ -2,8 +2,8 @@ # Maintainer: Andreas 'Segaja' Schleifer pkgname=(ruby-sinatra ruby-sinatra-contrib ruby-rack-protection) -pkgver=2.2.2 -pkgrel=2 +pkgver=3.0.6 +pkgrel=1 arch=('any') url='https://sinatrarb.com/' license=('MIT') @@ -14,29 +14,33 @@ checkdepends=( 'ruby-builder' 'ruby-erubi' 'ruby-kramdown' - 'ruby-maruku' 'ruby-mustermann' 'ruby-nokogiri' 'ruby-rack' 'ruby-rack-protection' 'ruby-rack-test' + 'ruby-rack-session' + 'ruby-rackup' 'ruby-rake' 'ruby-rdiscount' 'ruby-rspec' - 'ruby-sass' 'ruby-sinatra-contrib' 'ruby-tilt' # ruby-sinatra - integration tests + #'ruby-falcon' #'ruby-puma' #'ruby-thin' #'ruby-webrick' ) options=('!emptydirs') -source=("https://github.com/sinatra/sinatra/archive/v${pkgver}/sinatra-v${pkgver}.tar.gz") -sha512sums=('e69a36738a7c1095968a343113ddba414ff685e941a8b55575e4b2ffa5a8e7394d874966d5532643cdfec997ac4c825c293185f6688c715006e37159eedd2be8') +source=("https://github.com/sinatra/sinatra/archive/v${pkgver}/sinatra-v${pkgver}.tar.gz" + ruby-sinatra-3.0.6-ruby-rack-3.0.7-compat.patch) +sha512sums=('83643b634d2a992144ced65fc1466fded29bf6fae2e20f359b2d440c46013df26f4595f842cbed36319c5278beb77383eea6ac6eb4281f508501cee4e1c6e67a' + '9b504aaecc83d1e51a7d6c8e112fe679e31cc2e4568b66c2be12fe8ff9b3af5f33428ab33dc4f6c34fc914ae8d524da6722fa3a4471502bac3f05b632b0efa72') prepare() { cd "sinatra-${pkgver}" + patch -Np1 -i ../ruby-sinatra-3.0.6-ruby-rack-3.0.7-compat.patch # update gemspec to allow newer version of the dependencies sed --in-place --regexp-extended 's|~>|>=|g' sinatra.gemspec */*.gemspec @@ -59,31 +63,26 @@ build() { check() { cd "sinatra-${pkgver}" - # cannot load such file -- coffee-script: skipping coffee tests - # cannot load such file -- creole: skipping creole tests - # cannot load such file -- erubis: skipping erubis tests - # cannot load such file -- haml: skipping haml tests - # rainbows is not installed, skipping integration tests # rainbows is not installed, skipping integration tests - # reel is not installed, skipping integration tests + # puma is not installed, skipping integration tests + # cannot load such file -- liquid + # Did you mean? liquid_test: skipping liquid tests + # cannot load such file -- rabl: skipping rabl tests + # cannot load such file -- slim: skipping slim tests + # thin is not installed, skipping integration tests + # falcon is not installed, skipping integration tests + # puma is not installed, skipping integration tests # HTTP is not installed, skipping integration tests - # cannot load such file -- less: skipping less tests - # cannot load such file -- liquid: skipping liquid tests - # cannot load such file -- markaby: skipping markaby tests # cannot load such file -- pandoc-ruby: skipping markdown tests with Tilt::PandocTemplate # cannot load such file -- commonmarker: skipping markdown tests with Tilt::CommonMarkerTemplate # cannot load such file -- redcarpet: skipping markdown tests with Tilt::RedcarpetTemplate - # cannot load such file -- bluecloth: skipping markdown tests with Tilt::BlueClothTemplate - # cannot load such file -- wikicloth: skipping mediawiki tests - # cannot load such file -- rabl: skipping rabl tests - # cannot load such file -- radius: skipping radius tests - # cannot load such file -- slim: skipping slim tests - # cannot load such file -- stylus: skipping stylus tests - # cannot load such file -- redcloth: skipping textile tests - # cannot load such file -- wlang: skipping wlang tests - # cannot load such file -- yajl: skipping yajl tests + # cannot load such file -- yajl + # Did you mean? yaml: skipping yajl tests + # cannot load such file -- markaby + # Did you mean? markaby_test: skipping markaby tests + # cannot load such file -- haml: skipping haml tests echo "sinatra" - rake spec + rake test # disabled as it requires hamlit #(cd sinatra-contrib @@ -93,13 +92,14 @@ check() { (cd rack-protection echo "rack-protection" - rake spec + rake test ) } package_ruby-sinatra() { pkgdesc='Classy web-development dressed in a DSL' - depends=('ruby' 'ruby-mustermann' 'ruby-rack' 'ruby-rack-protection' 'ruby-tilt') + depends=('ruby' 'ruby-mustermann' 'ruby-rack' 'ruby-rack-protection' + 'ruby-rack-session' 'ruby-rackup' 'ruby-tilt') _package "sinatra" "LICENSE" } diff --git a/trunk/ruby-sinatra-3.0.6-ruby-rack-3.0.7-compat.patch b/trunk/ruby-sinatra-3.0.6-ruby-rack-3.0.7-compat.patch new file mode 100644 index 0000000..652018a --- /dev/null +++ b/trunk/ruby-sinatra-3.0.6-ruby-rack-3.0.7-compat.patch @@ -0,0 +1,1472 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Patrik Ragnarsson +Date: Fri, 23 Dec 2022 22:41:02 +0100 +Subject: [PATCH 01/19] Don't allow Rack 3 builds to fail + +--- + .github/workflows/test.yml | 20 +++++++++----------- + 1 file changed, 9 insertions(+), 11 deletions(-) + +diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml +index 5069cb8f..aa8eb08c 100644 +--- a/.github/workflows/test.yml ++++ b/.github/workflows/test.yml +@@ -23,25 +23,25 @@ jobs: + puma: + - stable + rack: +- - '~> 2' ++ - stable + tilt: + - stable + # Due to https://github.com/actions/runner/issues/849, we have to use quotes for '3.0' + ruby: [2.6, 2.7, '3.0', 3.1, 3.2, truffleruby] + include: + # Puma +- - { ruby: 3.1, rack: '~> 2', puma: '~> 5', tilt: stable } +- - { ruby: 3.2, rack: '~> 2', puma: head, tilt: stable, allow-failure: true } ++ - { ruby: 3.1, rack: stable, puma: '~> 5', tilt: stable } ++ - { ruby: 3.2, rack: stable, puma: head, tilt: stable, allow-failure: true } + # Tilt +- - { ruby: 3.2, rack: '~> 2', puma: stable, tilt: head, allow-failure: true } ++ - { ruby: 3.2, rack: stable, puma: stable, tilt: head, allow-failure: true } + # Due to flaky tests, see https://github.com/sinatra/sinatra/pull/1870 +- - { ruby: jruby-9.3, rack: '~> 2', puma: stable, tilt: stable, allow-failure: true } ++ - { ruby: jruby-9.3, rack: stable, puma: stable, tilt: stable, allow-failure: true } + # Due to https://github.com/jruby/jruby/issues/7647 +- - { ruby: jruby-9.4, rack: '~> 2', puma: stable, tilt: stable, allow-failure: true } ++ - { ruby: jruby-9.4, rack: stable, puma: stable, tilt: stable, allow-failure: true } + # Never fail our build due to problems with head +- - { ruby: ruby-head, rack: '~> 2', puma: stable, tilt: stable, allow-failure: true } +- - { ruby: jruby-head, rack: '~> 2', puma: stable, tilt: stable, allow-failure: true } +- - { ruby: truffleruby-head, rack: '~> 2', puma: stable, tilt: stable, allow-failure: true } ++ - { ruby: ruby-head, rack: stable, puma: stable, tilt: stable, allow-failure: true } ++ - { ruby: jruby-head, rack: stable, puma: stable, tilt: stable, allow-failure: true } ++ - { ruby: truffleruby-head, rack: stable, puma: stable, tilt: stable, allow-failure: true } + env: + rack: ${{ matrix.rack }} + puma: ${{ matrix.puma }} +@@ -56,7 +56,6 @@ jobs: + libxml2-dev \ + libxslt-dev \ + libyaml-dev +- + - uses: actions/checkout@v3 + + - uses: ruby/setup-ruby@v1 +@@ -78,7 +77,6 @@ jobs: + run: | + bundle install --jobs=3 --retry=3 + bundle exec rake +- + - name: Run rack-protection tests + continue-on-error: ${{ matrix.allow-failure || false }} + id: protection-tests +-- +2.39.2 + + +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Patrik Ragnarsson +Date: Sat, 24 Dec 2022 11:52:55 +0100 +Subject: [PATCH 02/19] Only use rainbows for Rack 2 + +--- + Gemfile | 4 +++- + test/integration/app.rb | 6 +++++- + 2 files changed, 8 insertions(+), 2 deletions(-) + +diff --git a/Gemfile b/Gemfile +index ea870323..cf0adce7 100644 +--- a/Gemfile ++++ b/Gemfile +@@ -46,7 +46,9 @@ gem 'markaby' + gem 'nokogiri', '> 1.5.0' + gem 'pandoc-ruby', '~> 2.0.2' + gem 'rabl' +-gem 'rainbows', platforms: [:mri] # uses #fork ++if rack_version == '~> 2' ++ gem 'rainbows', platforms: [:mri] # uses #fork ++end + gem 'rdiscount', platforms: [:ruby] + gem 'rdoc' + gem 'redcarpet', platforms: [:ruby] +diff --git a/test/integration/app.rb b/test/integration/app.rb +index dbc9e3a2..cd2a0b3c 100644 +--- a/test/integration/app.rb ++++ b/test/integration/app.rb +@@ -1,7 +1,11 @@ + $stderr.puts "loading" + require 'sinatra' + +-require_relative 'rainbows' if RUBY_ENGINE == 'ruby' ++begin ++ require_relative 'rainbows' if RUBY_ENGINE == 'ruby' ++rescue LoadError => e ++ warn "Skipping rainbows (#{e.inspect})" ++end + + configure do + set :foo, :bar +-- +2.39.2 + + +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Patrik Ragnarsson +Date: Sat, 24 Dec 2022 11:57:50 +0100 +Subject: [PATCH 03/19] Add `rackup`, `rack-session` as dependencies + +--- + sinatra.gemspec | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/sinatra.gemspec b/sinatra.gemspec +index 45c93b05..77680945 100644 +--- a/sinatra.gemspec ++++ b/sinatra.gemspec +@@ -46,7 +46,9 @@ RubyGems 2.0 or newer is required to protect against public gem pushes. You can + s.required_ruby_version = '>= 2.6.0' + + s.add_dependency 'mustermann', '~> 3.0' +- s.add_dependency 'rack', '~> 2.2', '>= 2.2.4' ++ s.add_dependency 'rack', '>= 2.2.4', '< 4' ++ s.add_dependency 'rackup', '>= 0.2.3', '< 1' ++ s.add_dependency 'rack-session', '>= 0.3.0', '< 1' + s.add_dependency 'rack-protection', version + s.add_dependency 'tilt', '~> 2.0' + +-- +2.39.2 + + +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Patrik Ragnarsson +Date: Sat, 24 Dec 2022 12:19:26 +0100 +Subject: [PATCH 04/19] Remove Rack 2 jobs + +Probably too hard to make Sinatra work with both Rack 2 and Rack 3 in +the same version. +--- + .github/workflows/test.yml | 1 + + sinatra.gemspec | 2 +- + 2 files changed, 2 insertions(+), 1 deletion(-) + +diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml +index aa8eb08c..10642939 100644 +--- a/.github/workflows/test.yml ++++ b/.github/workflows/test.yml +@@ -46,6 +46,7 @@ jobs: + rack: ${{ matrix.rack }} + puma: ${{ matrix.puma }} + tilt: ${{ matrix.tilt }} ++ + steps: + - name: Install dependencies + run: | +diff --git a/sinatra.gemspec b/sinatra.gemspec +index 77680945..956ecde3 100644 +--- a/sinatra.gemspec ++++ b/sinatra.gemspec +@@ -46,7 +46,7 @@ RubyGems 2.0 or newer is required to protect against public gem pushes. You can + s.required_ruby_version = '>= 2.6.0' + + s.add_dependency 'mustermann', '~> 3.0' +- s.add_dependency 'rack', '>= 2.2.4', '< 4' ++ s.add_dependency 'rack', '>= 3.0.0.beta1', '< 4' + s.add_dependency 'rackup', '>= 0.2.3', '< 1' + s.add_dependency 'rack-session', '>= 0.3.0', '< 1' + s.add_dependency 'rack-protection', version +-- +2.39.2 + + +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Patrik Ragnarsson +Date: Sat, 24 Dec 2022 12:26:38 +0100 +Subject: [PATCH 05/19] Run tests for sinatra-contrib and rack-protection + +--- + .github/workflows/test.yml | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml +index 10642939..57e7e3da 100644 +--- a/.github/workflows/test.yml ++++ b/.github/workflows/test.yml +@@ -67,12 +67,12 @@ jobs: + bundler-cache: true + + - name: Run sinatra tests +- continue-on-error: ${{ matrix.allow-failure || false }} ++ continue-on-error: true + id: tests + run: bundle exec rake + + - name: Run sinatra-contrib tests +- continue-on-error: ${{ matrix.allow-failure || false }} ++ continue-on-error: true + id: contrib-tests + working-directory: sinatra-contrib + run: | +-- +2.39.2 + + +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Patrik Ragnarsson +Date: Wed, 28 Dec 2022 12:55:29 +0100 +Subject: [PATCH 06/19] Rack 3 requires all response headers to be lowercase + +--- + lib/sinatra/base.rb | 34 +++++++++---------- + lib/sinatra/show_exceptions.rb | 4 +-- + rack-protection/lib/rack/protection/base.rb | 2 +- + .../protection/content_security_policy.rb | 4 +-- + .../lib/rack/protection/cookie_tossing.rb | 2 +- + .../lib/rack/protection/frame_options.rb | 2 +- + .../lib/rack/protection/json_csrf.rb | 2 +- + .../lib/rack/protection/referrer_policy.rb | 2 +- + .../lib/rack/protection/strict_transport.rb | 2 +- + .../lib/rack/protection/xss_header.rb | 4 +-- + .../protection/authenticity_token_spec.rb | 2 +- + .../content_security_policy_spec.rb | 2 +- + .../rack/protection/escaped_params_spec.rb | 10 +++--- + .../lib/rack/protection/frame_options_spec.rb | 2 +- + .../lib/rack/protection/json_csrf_spec.rb | 10 +++--- + .../rack/protection/path_traversal_spec.rb | 4 +-- + .../lib/rack/protection/protection_spec.rb | 4 +-- + .../lib/rack/protection/xss_header_spec.rb | 4 +-- + rack-protection/spec/support/dummy_app.rb | 2 +- + rack-protection/spec/support/spec_helpers.rb | 2 +- + test/response_test.rb | 2 +- + 21 files changed, 51 insertions(+), 51 deletions(-) + +diff --git a/lib/sinatra/base.rb b/lib/sinatra/base.rb +index c3ebf619..387130fd 100644 +--- a/lib/sinatra/base.rb ++++ b/lib/sinatra/base.rb +@@ -176,8 +176,8 @@ module Sinatra + result = body + + if drop_content_info? +- headers.delete 'Content-Length' +- headers.delete 'Content-Type' ++ headers.delete 'content-length' ++ headers.delete 'content-type' + end + + if drop_body? +@@ -186,9 +186,9 @@ module Sinatra + end + + if calculate_content_length? +- # if some other code has already set Content-Length, don't muck with it ++ # if some other code has already set content-length, don't muck with it + # currently, this would be the static file-handler +- headers['Content-Length'] = body.map(&:bytesize).reduce(0, :+).to_s ++ headers['content-length'] = body.map(&:bytesize).reduce(0, :+).to_s + end + + [status, headers, result] +@@ -197,7 +197,7 @@ module Sinatra + private + + def calculate_content_length? +- headers['Content-Type'] && !headers['Content-Length'] && (Array === body) ++ headers['content-type'] && !headers['content-length'] && (Array === body) + end + + def drop_content_info? +@@ -291,8 +291,8 @@ module Sinatra + elsif value + # Rack 2.0 returns a Rack::File::Iterator here instead of + # Rack::File as it was in the previous API. +- unless request.head? || value.is_a?(Rack::Files::Iterator) || value.is_a?(Stream) +- headers.delete 'Content-Length' ++ unless request.head? || value.is_a?(Rack::File::Iterator) || value.is_a?(Stream) ++ headers.delete 'content-length' + end + response.body = value + else +@@ -372,10 +372,10 @@ module Sinatra + Base.mime_type(type) + end + +- # Set the Content-Type of the response body given a media type or file ++ # Set the content-type of the response body given a media type or file + # extension. + def content_type(type = nil, params = {}) +- return response['Content-Type'] unless type ++ return response['content-type'] unless type + + default = params.delete :default + mime_type = mime_type(type) || default +@@ -393,7 +393,7 @@ module Sinatra + "#{key}=#{val}" + end.join(', ') + end +- response['Content-Type'] = mime_type ++ response['content-type'] = mime_type + end + + # https://html.spec.whatwg.org/#multipart-form-data +@@ -412,12 +412,12 @@ module Sinatra + params = format('; filename="%s"', File.basename(filename).gsub(/["\r\n]/, MULTIPART_FORM_DATA_REPLACEMENT_TABLE)) + response['Content-Disposition'] << params + ext = File.extname(filename) +- content_type(ext) unless response['Content-Type'] || ext.empty? ++ content_type(ext) unless response['content-type'] || ext.empty? + end + + # Use the contents of the file at +path+ as the response body. + def send_file(path, opts = {}) +- if opts[:type] || !response['Content-Type'] ++ if opts[:type] || !response['content-type'] + content_type opts[:type] || File.extname(path), default: 'application/octet-stream' + end + +@@ -433,7 +433,7 @@ module Sinatra + result = file.serving(request, path) + + result[1].each { |k, v| headers[k] ||= v } +- headers['Content-Length'] = result[1]['Content-Length'] ++ headers['content-length'] = result[1]['content-length'] + opts[:status] &&= Integer(opts[:status]) + halt (opts[:status] || result[0]), result[2] + rescue Errno::ENOENT +@@ -949,7 +949,7 @@ module Sinatra + invoke { dispatch! } + invoke { error_block!(response.status) } unless @env['sinatra.error'] + +- unless @response['Content-Type'] ++ unless @response['content-type'] + if Array === body && body[0].respond_to?(:content_type) + content_type body[0].content_type + elsif (default = settings.default_content_type) +@@ -1012,7 +1012,7 @@ module Sinatra + routes = base.routes[@request.request_method] + + routes&.each do |pattern, conditions, block| +- response.delete_header('Content-Type') unless @pinned_response ++ response.delete_header('content-type') unless @pinned_response + + returned_pass_block = process_route(pattern, conditions) do |*args| + env['sinatra.route'] = "#{@request.request_method} #{pattern}" +@@ -1133,7 +1133,7 @@ module Sinatra + invoke do + static! if settings.static? && (request.get? || request.head?) + filter! :before do +- @pinned_response = !response['Content-Type'].nil? ++ @pinned_response = !response['content-type'].nil? + end + route! + end +@@ -1655,7 +1655,7 @@ module Sinatra + types.map! { |t| mime_types(t) } + types.flatten! + condition do +- response_content_type = response['Content-Type'] ++ response_content_type = response['content-type'] + preferred_type = request.preferred_type(types) + + if response_content_type +diff --git a/lib/sinatra/show_exceptions.rb b/lib/sinatra/show_exceptions.rb +index db847ffa..293e88fb 100644 +--- a/lib/sinatra/show_exceptions.rb ++++ b/lib/sinatra/show_exceptions.rb +@@ -38,8 +38,8 @@ module Sinatra + [ + 500, + { +- 'Content-Type' => content_type, +- 'Content-Length' => body.bytesize.to_s ++ 'content-type' => content_type, ++ 'content-length' => body.bytesize.to_s + }, + [body] + ] +diff --git a/rack-protection/lib/rack/protection/base.rb b/rack-protection/lib/rack/protection/base.rb +index a1314adb..ed570d68 100644 +--- a/rack-protection/lib/rack/protection/base.rb ++++ b/rack-protection/lib/rack/protection/base.rb +@@ -74,7 +74,7 @@ module Rack + + def deny(env) + warn env, "attack prevented by #{self.class}" +- [options[:status], { 'Content-Type' => 'text/plain' }, [options[:message]]] ++ [options[:status], { 'content-type' => 'text/plain' }, [options[:message]]] + end + + def report(env) +diff --git a/rack-protection/lib/rack/protection/content_security_policy.rb b/rack-protection/lib/rack/protection/content_security_policy.rb +index 32d8ac70..d85f28b4 100644 +--- a/rack-protection/lib/rack/protection/content_security_policy.rb ++++ b/rack-protection/lib/rack/protection/content_security_policy.rb +@@ -26,7 +26,7 @@ module Rack + # https://scotthelme.co.uk/csp-cheat-sheet/ + # http://www.html5rocks.com/en/tutorials/security/content-security-policy/ + # +- # Sets the 'Content-Security-Policy[-Report-Only]' header. ++ # Sets the 'content-security-policy[-report-only]' header. + # + # Options: ContentSecurityPolicy configuration is a complex topic with + # several levels of support that has evolved over time. +@@ -71,7 +71,7 @@ module Rack + + def call(env) + status, headers, body = @app.call(env) +- header = options[:report_only] ? 'Content-Security-Policy-Report-Only' : 'Content-Security-Policy' ++ header = options[:report_only] ? 'content-security-policy-report-only' : 'content-security-policy' + headers[header] ||= csp_policy if html? headers + [status, headers, body] + end +diff --git a/rack-protection/lib/rack/protection/cookie_tossing.rb b/rack-protection/lib/rack/protection/cookie_tossing.rb +index 0a7f43ad..fbef360a 100644 +--- a/rack-protection/lib/rack/protection/cookie_tossing.rb ++++ b/rack-protection/lib/rack/protection/cookie_tossing.rb +@@ -51,7 +51,7 @@ module Rack + def redirect(env) + request = Request.new(env) + warn env, "attack prevented by #{self.class}" +- [302, { 'Content-Type' => 'text/html', 'Location' => request.path }, []] ++ [302, { 'content-type' => 'text/html', 'location' => request.path }, []] + end + + def bad_cookies +diff --git a/rack-protection/lib/rack/protection/frame_options.rb b/rack-protection/lib/rack/protection/frame_options.rb +index c159d4a9..80e7fc1e 100644 +--- a/rack-protection/lib/rack/protection/frame_options.rb ++++ b/rack-protection/lib/rack/protection/frame_options.rb +@@ -31,7 +31,7 @@ module Rack + + def call(env) + status, headers, body = @app.call(env) +- headers['X-Frame-Options'] ||= frame_options if html? headers ++ headers['x-frame-options'] ||= frame_options if html? headers + [status, headers, body] + end + end +diff --git a/rack-protection/lib/rack/protection/json_csrf.rb b/rack-protection/lib/rack/protection/json_csrf.rb +index 9f6817ac..59d8e496 100644 +--- a/rack-protection/lib/rack/protection/json_csrf.rb ++++ b/rack-protection/lib/rack/protection/json_csrf.rb +@@ -39,7 +39,7 @@ module Rack + def has_vector?(request, headers) + return false if request.xhr? + return false if options[:allow_if]&.call(request.env) +- return false unless headers['Content-Type'].to_s.split(';', 2).first =~ %r{^\s*application/json\s*$} ++ return false unless headers['content-type'].to_s.split(';', 2).first =~ %r{^\s*application/json\s*$} + + origin(request.env).nil? and referrer(request.env) != request.host + end +diff --git a/rack-protection/lib/rack/protection/referrer_policy.rb b/rack-protection/lib/rack/protection/referrer_policy.rb +index eaff7020..c5c44f42 100644 +--- a/rack-protection/lib/rack/protection/referrer_policy.rb ++++ b/rack-protection/lib/rack/protection/referrer_policy.rb +@@ -19,7 +19,7 @@ module Rack + + def call(env) + status, headers, body = @app.call(env) +- headers['Referrer-Policy'] ||= options[:referrer_policy] ++ headers['referrer-policy'] ||= options[:referrer_policy] + [status, headers, body] + end + end +diff --git a/rack-protection/lib/rack/protection/strict_transport.rb b/rack-protection/lib/rack/protection/strict_transport.rb +index 05fe4ae6..40e45a68 100644 +--- a/rack-protection/lib/rack/protection/strict_transport.rb ++++ b/rack-protection/lib/rack/protection/strict_transport.rb +@@ -33,7 +33,7 @@ module Rack + + def call(env) + status, headers, body = @app.call(env) +- headers['Strict-Transport-Security'] ||= strict_transport ++ headers['strict-transport-security'] ||= strict_transport + [status, headers, body] + end + end +diff --git a/rack-protection/lib/rack/protection/xss_header.rb b/rack-protection/lib/rack/protection/xss_header.rb +index 14c679d9..8eee599b 100644 +--- a/rack-protection/lib/rack/protection/xss_header.rb ++++ b/rack-protection/lib/rack/protection/xss_header.rb +@@ -18,8 +18,8 @@ module Rack + + def call(env) + status, headers, body = @app.call(env) +- headers['X-XSS-Protection'] ||= "1; mode=#{options[:xss_mode]}" if html? headers +- headers['X-Content-Type-Options'] ||= 'nosniff' if options[:nosniff] ++ headers['x-xss-protection'] ||= "1; mode=#{options[:xss_mode]}" if html? headers ++ headers['x-content-type-options'] ||= 'nosniff' if options[:nosniff] + [status, headers, body] + end + end +diff --git a/rack-protection/spec/lib/rack/protection/authenticity_token_spec.rb b/rack-protection/spec/lib/rack/protection/authenticity_token_spec.rb +index e589726b..adf64f1b 100644 +--- a/rack-protection/spec/lib/rack/protection/authenticity_token_spec.rb ++++ b/rack-protection/spec/lib/rack/protection/authenticity_token_spec.rb +@@ -61,7 +61,7 @@ RSpec.describe Rack::Protection::AuthenticityToken do + it 'allows for a custom authenticity token param' do + mock_app do + use Rack::Protection::AuthenticityToken, authenticity_param: 'csrf_param' +- run proc { |_e| [200, { 'Content-Type' => 'text/plain' }, ['hi']] } ++ run proc { |_e| [200, { 'content-type' => 'text/plain' }, ['hi']] } + end + + post('/', { 'csrf_param' => token }, 'rack.session' => { csrf: token }) +diff --git a/rack-protection/spec/lib/rack/protection/content_security_policy_spec.rb b/rack-protection/spec/lib/rack/protection/content_security_policy_spec.rb +index 35a26ad8..0d26c14a 100644 +--- a/rack-protection/spec/lib/rack/protection/content_security_policy_spec.rb ++++ b/rack-protection/spec/lib/rack/protection/content_security_policy_spec.rb +@@ -62,7 +62,7 @@ RSpec.describe Rack::Protection::ContentSecurityPolicy do + end + + it 'should not override the header if already set' do +- mock_app with_headers('Content-Security-Policy' => 'default-src: none') ++ mock_app with_headers('content-security-policy' => 'default-src: none') + expect(get('/', {}, 'wants' => 'text/html').headers['Content-Security-Policy']).to eq('default-src: none') + end + end +diff --git a/rack-protection/spec/lib/rack/protection/escaped_params_spec.rb b/rack-protection/spec/lib/rack/protection/escaped_params_spec.rb +index d6b1727b..f99582f6 100644 +--- a/rack-protection/spec/lib/rack/protection/escaped_params_spec.rb ++++ b/rack-protection/spec/lib/rack/protection/escaped_params_spec.rb +@@ -7,7 +7,7 @@ RSpec.describe Rack::Protection::EscapedParams do + it 'escapes html entities' do + mock_app do |env| + request = Rack::Request.new(env) +- [200, { 'Content-Type' => 'text/plain' }, [request.params['foo']]] ++ [200, { 'content-type' => 'text/plain' }, [request.params['foo']]] + end + get '/', foo: '' + expect(body).to eq('<bar>') +@@ -16,7 +16,7 @@ RSpec.describe Rack::Protection::EscapedParams do + it 'leaves normal params untouched' do + mock_app do |env| + request = Rack::Request.new(env) +- [200, { 'Content-Type' => 'text/plain' }, [request.params['foo']]] ++ [200, { 'content-type' => 'text/plain' }, [request.params['foo']]] + end + get '/', foo: 'bar' + expect(body).to eq('bar') +@@ -25,7 +25,7 @@ RSpec.describe Rack::Protection::EscapedParams do + it 'copes with nested arrays' do + mock_app do |env| + request = Rack::Request.new(env) +- [200, { 'Content-Type' => 'text/plain' }, [request.params['foo']['bar']]] ++ [200, { 'content-type' => 'text/plain' }, [request.params['foo']['bar']]] + end + get '/', foo: { bar: '' } + expect(body).to eq('<bar>') +@@ -33,7 +33,7 @@ RSpec.describe Rack::Protection::EscapedParams do + + it 'leaves cache-breaker params untouched' do + mock_app do |_env| +- [200, { 'Content-Type' => 'text/plain' }, ['hi']] ++ [200, { 'content-type' => 'text/plain' }, ['hi']] + end + + get '/?95df8d9bf5237ad08df3115ee74dcb10' +@@ -43,7 +43,7 @@ RSpec.describe Rack::Protection::EscapedParams do + it 'leaves TempFiles untouched' do + mock_app do |env| + request = Rack::Request.new(env) +- [200, { 'Content-Type' => 'text/plain' }, ["#{request.params['file'][:filename]}\n#{request.params['file'][:tempfile].read}\n#{request.params['other']}"]] ++ [200, { 'content-type' => 'text/plain' }, ["#{request.params['file'][:filename]}\n#{request.params['file'][:tempfile].read}\n#{request.params['other']}"]] + end + + temp_file = File.open('_escaped_params_tmp_file', 'w') +diff --git a/rack-protection/spec/lib/rack/protection/frame_options_spec.rb b/rack-protection/spec/lib/rack/protection/frame_options_spec.rb +index 23ba8f11..aa516072 100644 +--- a/rack-protection/spec/lib/rack/protection/frame_options_spec.rb ++++ b/rack-protection/spec/lib/rack/protection/frame_options_spec.rb +@@ -32,7 +32,7 @@ RSpec.describe Rack::Protection::FrameOptions do + end + + it 'should not override the header if already set' do +- mock_app with_headers('X-Frame-Options' => 'allow') ++ mock_app with_headers('x-frame-options' => 'allow') + expect(get('/', {}, 'wants' => 'text/html').headers['X-Frame-Options']).to eq('allow') + end + end +diff --git a/rack-protection/spec/lib/rack/protection/json_csrf_spec.rb b/rack-protection/spec/lib/rack/protection/json_csrf_spec.rb +index 06312665..59167656 100644 +--- a/rack-protection/spec/lib/rack/protection/json_csrf_spec.rb ++++ b/rack-protection/spec/lib/rack/protection/json_csrf_spec.rb +@@ -24,13 +24,13 @@ RSpec.describe Rack::Protection::JsonCsrf do + + def self.call(env) + Thread.current[:last_env] = env +- [200, { 'Content-Type' => 'application/json' }, body] ++ [200, { 'content-type' => 'application/json' }, body] + end + end + + describe 'json response' do + before do +- mock_app { |_e| [200, { 'Content-Type' => 'application/json' }, []] } ++ mock_app { |_e| [200, { 'content-type' => 'application/json' }, []] } + end + + it 'denies get requests with json responses with a remote referrer' do +@@ -39,7 +39,7 @@ RSpec.describe Rack::Protection::JsonCsrf do + + it 'closes the body returned by the app if it denies the get request' do + mock_app DummyAppWithBody do |_e| +- [200, { 'Content-Type' => 'application/json' }, []] ++ [200, { 'content-type' => 'application/json' }, []] + end + + get('/', {}, 'HTTP_REFERER' => 'http://evil.com') +@@ -50,7 +50,7 @@ RSpec.describe Rack::Protection::JsonCsrf do + it 'accepts requests with json responses with a remote referrer when allow_if is true' do + mock_app do + use Rack::Protection::JsonCsrf, allow_if: ->(env) { env['HTTP_REFERER'] == 'http://good.com' } +- run proc { |_e| [200, { 'Content-Type' => 'application/json' }, []] } ++ run proc { |_e| [200, { 'content-type' => 'application/json' }, []] } + end + + expect(get('/', {}, 'HTTP_REFERER' => 'http://good.com')).to be_ok +@@ -88,7 +88,7 @@ RSpec.describe Rack::Protection::JsonCsrf do + it 'still denies' do + mock_app do + use Rack::Protection, reaction: :drop_session +- run proc { |_e| [200, { 'Content-Type' => 'application/json' }, []] } ++ run proc { |_e| [200, { 'content-type' => 'application/json' }, []] } + end + + session = { foo: :bar } +diff --git a/rack-protection/spec/lib/rack/protection/path_traversal_spec.rb b/rack-protection/spec/lib/rack/protection/path_traversal_spec.rb +index 1eca34bd..a5921f15 100644 +--- a/rack-protection/spec/lib/rack/protection/path_traversal_spec.rb ++++ b/rack-protection/spec/lib/rack/protection/path_traversal_spec.rb +@@ -5,7 +5,7 @@ RSpec.describe Rack::Protection::PathTraversal do + + context 'escaping' do + before do +- mock_app { |e| [200, { 'Content-Type' => 'text/plain' }, [e['PATH_INFO']]] } ++ mock_app { |e| [200, { 'content-type' => 'text/plain' }, [e['PATH_INFO']]] } + end + + %w[/foo/bar /foo/bar/ / /.f /a.x].each do |path| +@@ -28,7 +28,7 @@ RSpec.describe Rack::Protection::PathTraversal do + + context "PATH_INFO's encoding" do + before do +- @app = Rack::Protection::PathTraversal.new(proc { |e| [200, { 'Content-Type' => 'text/plain' }, [e['PATH_INFO'].encoding.to_s]] }) ++ @app = Rack::Protection::PathTraversal.new(proc { |e| [200, { 'content-type' => 'text/plain' }, [e['PATH_INFO'].encoding.to_s]] }) + end + + it 'should remain unchanged as ASCII-8BIT' do +diff --git a/rack-protection/spec/lib/rack/protection/protection_spec.rb b/rack-protection/spec/lib/rack/protection/protection_spec.rb +index e8dc55df..5213c870 100644 +--- a/rack-protection/spec/lib/rack/protection/protection_spec.rb ++++ b/rack-protection/spec/lib/rack/protection/protection_spec.rb +@@ -6,7 +6,7 @@ RSpec.describe Rack::Protection do + it 'passes on options' do + mock_app do + use Rack::Protection, track: ['HTTP_FOO'] +- run proc { |_e| [200, { 'Content-Type' => 'text/plain' }, ['hi']] } ++ run proc { |_e| [200, { 'content-type' => 'text/plain' }, ['hi']] } + end + + session = { foo: :bar } +@@ -21,7 +21,7 @@ RSpec.describe Rack::Protection do + it 'passes errors through if :reaction => :report is used' do + mock_app do + use Rack::Protection, reaction: :report +- run proc { |e| [200, { 'Content-Type' => 'text/plain' }, [e['protection.failed'].to_s]] } ++ run proc { |e| [200, { 'content-type' => 'text/plain' }, [e['protection.failed'].to_s]] } + end + + session = { foo: :bar } +diff --git a/rack-protection/spec/lib/rack/protection/xss_header_spec.rb b/rack-protection/spec/lib/rack/protection/xss_header_spec.rb +index 2fef0edb..bd98eef6 100644 +--- a/rack-protection/spec/lib/rack/protection/xss_header_spec.rb ++++ b/rack-protection/spec/lib/rack/protection/xss_header_spec.rb +@@ -26,7 +26,7 @@ RSpec.describe Rack::Protection::XSSHeader do + end + + it 'should not override the header if already set' do +- mock_app with_headers('X-XSS-Protection' => '0') ++ mock_app with_headers('x-xss-protection' => '0') + expect(get('/', {}, 'wants' => 'text/html').headers['X-XSS-Protection']).to eq('0') + end + +@@ -48,7 +48,7 @@ RSpec.describe Rack::Protection::XSSHeader do + end + + it 'should not override the header if already set X-Content-Type-Options' do +- mock_app with_headers('X-Content-Type-Options' => 'sniff') ++ mock_app with_headers('x-content-type-options' => 'sniff') + expect(get('/', {}, 'wants' => 'text/html').headers['X-Content-Type-Options']).to eq('sniff') + end + end +diff --git a/rack-protection/spec/support/dummy_app.rb b/rack-protection/spec/support/dummy_app.rb +index fb83d36b..0b73ae57 100644 +--- a/rack-protection/spec/support/dummy_app.rb ++++ b/rack-protection/spec/support/dummy_app.rb +@@ -4,6 +4,6 @@ module DummyApp + def self.call(env) + Thread.current[:last_env] = env + body = (env['REQUEST_METHOD'] == 'HEAD' ? '' : 'ok') +- [200, { 'Content-Type' => env['wants'] || 'text/plain' }, [body]] ++ [200, { 'content-type' => env['wants'] || 'text/plain' }, [body]] + end + end +diff --git a/rack-protection/spec/support/spec_helpers.rb b/rack-protection/spec/support/spec_helpers.rb +index d9f40a27..4a47bb68 100644 +--- a/rack-protection/spec/support/spec_helpers.rb ++++ b/rack-protection/spec/support/spec_helpers.rb +@@ -29,7 +29,7 @@ module SpecHelpers + end + + def with_headers(headers) +- proc { [200, { 'Content-Type' => 'text/plain' }.merge(headers), ['ok']] } ++ proc { [200, { 'content-type' => 'text/plain' }.merge(headers), ['ok']] } + end + + def env +diff --git a/test/response_test.rb b/test/response_test.rb +index 5c1e6057..5c85330d 100644 +--- a/test/response_test.rb ++++ b/test/response_test.rb +@@ -40,7 +40,7 @@ class ResponseTest < Minitest::Test + @response.body = ['Hello World'] + assert_equal [ + status_code, +- { 'Content-Type' => 'text/html', 'Content-Length' => '11' }, ++ { 'content-type' => 'text/html', 'content-length' => '11' }, + ['Hello World'] + ], @response.finish + end +-- +2.39.2 + + +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Patrik Ragnarsson +Date: Wed, 28 Dec 2022 18:22:42 +0100 +Subject: [PATCH 07/19] `SERVER_PROTOCOL` -> `HTTP_VERSION` + +Similar change as https://github.com/jeremyevans/roda/commit/50f0ddf06728f8fd1b460e7a643eb48025d2ef17 + +Good to know is that rack-test defaults to HTTP/1.0: +https://github.com/rack/rack-test/blob/v2.0.2/lib/rack/test.rb#L277-L285 +(so Rack::Lint does not catch all usage of HTTP_VERSION) +--- + lib/sinatra/base.rb | 5 ++++- + .../spec/lib/rack/protection/session_hijacking_spec.rb | 4 ++-- + test/helpers_test.rb | 4 ++-- + 3 files changed, 8 insertions(+), 5 deletions(-) + +diff --git a/lib/sinatra/base.rb b/lib/sinatra/base.rb +index 387130fd..4914cbd5 100644 +--- a/lib/sinatra/base.rb ++++ b/lib/sinatra/base.rb +@@ -302,7 +302,10 @@ module Sinatra + + # Halt processing and redirect to the URI provided. + def redirect(uri, *args) +- if (env['HTTP_VERSION'] == 'HTTP/1.1') && (env['REQUEST_METHOD'] != 'GET') ++ # SERVER_PROTOCOL is required in Rack 3, fall back to HTTP_VERSION ++ # for servers not updated for Rack 3 (like Puma 5) ++ http_version = env['SERVER_PROTOCOL'] || env['HTTP_VERSION'] ++ if (http_version == 'HTTP/1.1') && (env['REQUEST_METHOD'] != 'GET') + status 303 + else + status 302 +diff --git a/rack-protection/spec/lib/rack/protection/session_hijacking_spec.rb b/rack-protection/spec/lib/rack/protection/session_hijacking_spec.rb +index e39497b1..508b5814 100644 +--- a/rack-protection/spec/lib/rack/protection/session_hijacking_spec.rb ++++ b/rack-protection/spec/lib/rack/protection/session_hijacking_spec.rb +@@ -27,8 +27,8 @@ RSpec.describe Rack::Protection::SessionHijacking do + + it 'accepts requests with a changing Version header' do + session = { foo: :bar } +- get '/', {}, 'rack.session' => session, 'HTTP_VERSION' => '1.0' +- get '/', {}, 'rack.session' => session, 'HTTP_VERSION' => '1.1' ++ get '/', {}, 'rack.session' => session, 'SERVER_PROTOCOL' => 'HTTP/1.0' ++ get '/', {}, 'rack.session' => session, 'SERVER_PROTOCOL' => 'HTTP/1.1' + expect(session[:foo]).to eq(:bar) + end + end +diff --git a/test/helpers_test.rb b/test/helpers_test.rb +index ee1298b2..04117610 100644 +--- a/test/helpers_test.rb ++++ b/test/helpers_test.rb +@@ -236,7 +236,7 @@ class HelpersTest < Minitest::Test + + it 'uses 303 for post requests if request is HTTP 1.1' do + mock_app { post('/') { redirect '/'} } +- post('/', {}, 'HTTP_VERSION' => 'HTTP/1.1') ++ post('/', {}, 'SERVER_PROTOCOL' => 'HTTP/1.1') + assert_equal 303, status + assert_equal '', body + assert_equal 'http://example.org/', response['Location'] +@@ -244,7 +244,7 @@ class HelpersTest < Minitest::Test + + it 'uses 302 for post requests if request is HTTP 1.0' do + mock_app { post('/') { redirect '/'} } +- post('/', {}, 'HTTP_VERSION' => 'HTTP/1.0') ++ post('/', {}, 'SERVER_PROTOCOL' => 'HTTP/1.0') + assert_equal 302, status + assert_equal '', body + assert_equal 'http://example.org/', response['Location'] +-- +2.39.2 + + +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Patrik Ragnarsson +Date: Wed, 28 Dec 2022 18:43:11 +0100 +Subject: [PATCH 08/19] Rack 3 does not allow newlines in headers + +See https://github.com/rack/rack/issues/1598, https://github.com/rack/rack/pull/1793 +--- + sinatra-contrib/lib/sinatra/link_header.rb | 8 ++++---- + sinatra-contrib/spec/link_header_spec.rb | 2 +- + 2 files changed, 5 insertions(+), 5 deletions(-) + +diff --git a/sinatra-contrib/lib/sinatra/link_header.rb b/sinatra-contrib/lib/sinatra/link_header.rb +index fcf1dcb4..022c26bb 100644 +--- a/sinatra-contrib/lib/sinatra/link_header.rb ++++ b/sinatra-contrib/lib/sinatra/link_header.rb +@@ -89,10 +89,10 @@ module Sinatra + link = (response['Link'] ||= '') + + urls.map do |url| +- link << ",\n" unless link.empty? ++ link << "," unless link.empty? + link << (http_pattern % url) + html_pattern % url +- end.join "\n" ++ end.join + end + + ## +@@ -117,10 +117,10 @@ module Sinatra + yield if block_given? + return '' unless response.include? 'Link' + +- response['Link'].split(",\n").map do |line| ++ response['Link'].split(",").map do |line| + url, *opts = line.split(';').map(&:strip) + "" +- end.join "\n" ++ end.join + end + + def self.registered(_base) +diff --git a/sinatra-contrib/spec/link_header_spec.rb b/sinatra-contrib/spec/link_header_spec.rb +index f18cabb0..dfd07c8e 100644 +--- a/sinatra-contrib/spec/link_header_spec.rb ++++ b/sinatra-contrib/spec/link_header_spec.rb +@@ -41,7 +41,7 @@ RSpec.describe Sinatra::LinkHeader do + it "takes an options hash" do + get '/' + elements = ["", "foo=\"bar\"", "rel=\"from-filter\""] +- expect(headers['Link'].split(",\n").first.strip.split('; ').sort).to eq(elements) ++ expect(headers['Link'].split(",").first.strip.split('; ').sort).to eq(elements) + end + end + +-- +2.39.2 + + +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Patrik Ragnarsson +Date: Wed, 28 Dec 2022 18:52:13 +0100 +Subject: [PATCH 09/19] Find `RACK_SESSION_UNPACKED_COOKIE_DATA` + +It moved to Rack::Session: +https://github.com/rack/rack-session/blob/v0.3.0/lib/rack/session/constants.rb +--- + rack-protection/lib/rack/protection/encrypted_cookie.rb | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/rack-protection/lib/rack/protection/encrypted_cookie.rb b/rack-protection/lib/rack/protection/encrypted_cookie.rb +index bf682312..006d496a 100644 +--- a/rack-protection/lib/rack/protection/encrypted_cookie.rb ++++ b/rack-protection/lib/rack/protection/encrypted_cookie.rb +@@ -202,7 +202,7 @@ module Rack + end + + def unpacked_cookie_data(request) +- request.fetch_header(RACK_SESSION_UNPACKED_COOKIE_DATA) do |k| ++ request.fetch_header(Session::RACK_SESSION_UNPACKED_COOKIE_DATA) do |k| + session_data = cookie_data = request.cookies[@key] + + # Try to decrypt with the first secret, if that returns nil, try +-- +2.39.2 + + +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Patrik Ragnarsson +Date: Wed, 28 Dec 2022 21:31:44 +0100 +Subject: [PATCH 10/19] rack-protection tests now pass (on Ruby 2.7.7) + +--- + rack-protection/lib/rack/protection.rb | 1 + + rack-protection/spec/lib/rack/protection/cookie_tossing_spec.rb | 2 +- + 2 files changed, 2 insertions(+), 1 deletion(-) + +diff --git a/rack-protection/lib/rack/protection.rb b/rack-protection/lib/rack/protection.rb +index 21d2ec92..b4a5429e 100644 +--- a/rack-protection/lib/rack/protection.rb ++++ b/rack-protection/lib/rack/protection.rb +@@ -2,6 +2,7 @@ + + require 'rack/protection/version' + require 'rack' ++require 'rack/session' + + module Rack + module Protection +diff --git a/rack-protection/spec/lib/rack/protection/cookie_tossing_spec.rb b/rack-protection/spec/lib/rack/protection/cookie_tossing_spec.rb +index f24b9920..9e90b438 100644 +--- a/rack-protection/spec/lib/rack/protection/cookie_tossing_spec.rb ++++ b/rack-protection/spec/lib/rack/protection/cookie_tossing_spec.rb +@@ -29,7 +29,7 @@ RSpec.describe Rack::Protection::CookieTossing do + it 'adds the correct Set-Cookie header' do + get '/some/path', {}, 'HTTP_COOKIE' => 'rack.%73ession=EVIL_SESSION_TOKEN; rack.session=EVIL_SESSION_TOKEN; rack.session=SESSION_TOKEN' + +- expected_header = <<-END.chomp ++ expected_header = <<-END.chomp.split("\n") + rack.%2573ession=; domain=example.org; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT + rack.%2573ession=; domain=example.org; path=/some; expires=Thu, 01 Jan 1970 00:00:00 GMT + rack.%2573ession=; domain=example.org; path=/some/path; expires=Thu, 01 Jan 1970 00:00:00 GMT +-- +2.39.2 + + +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Patrik Ragnarsson +Date: Wed, 28 Dec 2022 21:44:10 +0100 +Subject: [PATCH 11/19] Cookie attributes are lowercase in Rack 3 + +See https://github.com/rack/rack/pull/1849, https://github.com/rack/rack/issues/1848 +--- + sinatra-contrib/spec/cookies_spec.rb | 12 ++++++------ + 1 file changed, 6 insertions(+), 6 deletions(-) + +diff --git a/sinatra-contrib/spec/cookies_spec.rb b/sinatra-contrib/spec/cookies_spec.rb +index 852f6425..bab46365 100644 +--- a/sinatra-contrib/spec/cookies_spec.rb ++++ b/sinatra-contrib/spec/cookies_spec.rb +@@ -93,7 +93,7 @@ RSpec.describe Sinatra::Cookies do + expect(cookie_route do + cookies['foo'] = 'bar' + response['Set-Cookie'].lines.detect { |l| l.start_with? 'foo=' } +- end).to include('HttpOnly') ++ end).to include('httponly') + end + + it 'sets domain to nil if localhost' do +@@ -217,7 +217,7 @@ RSpec.describe Sinatra::Cookies do + cookies.delete 'foo' + response['Set-Cookie'] + end +- expect(cookie_header).to include("path=/foo;", "domain=bar.com;", "secure;", "HttpOnly") ++ expect(cookie_header).to include("path=/foo;", "domain=bar.com;", "secure;", "httponly") + end + + it 'does not touch other cookies' do +@@ -724,20 +724,20 @@ RSpec.describe Sinatra::Cookies do + expect(cookie_jar['foo']).to eq('bar') + end + +- it 'sets a cookie with HttpOnly' do ++ it 'sets a cookie with httponly' do + expect(cookie_route do + request.script_name = '/foo' + cookies.set('foo', value: 'bar', httponly: true) + response['Set-Cookie'].lines.detect { |l| l.start_with? 'foo=' } +- end).to include('HttpOnly') ++ end).to include('httponly') + end + +- it 'sets a cookie without HttpOnly' do ++ it 'sets a cookie without httponly' do + expect(cookie_route do + request.script_name = '/foo' + cookies.set('foo', value: 'bar', httponly: false) + response['Set-Cookie'].lines.detect { |l| l.start_with? 'foo=' } +- end).not_to include('HttpOnly') ++ end).not_to include('httponly') + end + end + +-- +2.39.2 + + +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Patrik Ragnarsson +Date: Thu, 29 Dec 2022 14:36:32 +0100 +Subject: [PATCH 12/19] sinatra-contrib tests now pass + +Multiple response header values are encoded using an Array instead of +newlines: https://github.com/rack/rack/blob/v3.0.3/UPGRADE-GUIDE.md#multiple-response-header-values-are-encoded-using-an-array + +Rack 3 does not remove cookies from the internal storage (because it +doesn't make much sense), see https://github.com/rack/rack/pull/1844, https://github.com/rack/rack/issues/1840 +--- + sinatra-contrib/lib/sinatra/cookies.rb | 10 +++++----- + sinatra-contrib/spec/cookies_spec.rb | 25 +++++++++++++----------- + sinatra-contrib/spec/link_header_spec.rb | 2 +- + 3 files changed, 20 insertions(+), 17 deletions(-) + +diff --git a/sinatra-contrib/lib/sinatra/cookies.rb b/sinatra-contrib/lib/sinatra/cookies.rb +index 742f45d7..d6303ce8 100644 +--- a/sinatra-contrib/lib/sinatra/cookies.rb ++++ b/sinatra-contrib/lib/sinatra/cookies.rb +@@ -60,7 +60,7 @@ module Sinatra + attr_reader :options + + def initialize(app) +- @response_string = nil ++ @response_array = nil + @response_hash = {} + @response = app.response + @request = app.request +@@ -309,12 +309,12 @@ module Sinatra + end + + def parse_response +- string = @response['Set-Cookie'] +- return if @response_string == string ++ cookies_from_response = Array(@response['Set-Cookie']) ++ return if @response_array == cookies_from_response + + hash = {} + +- string.each_line do |line| ++ cookies_from_response.each do |line| + key, value = line.split(';', 2).first.to_s.split('=', 2) + next if key.nil? + +@@ -328,7 +328,7 @@ module Sinatra + end + + @response_hash.replace hash +- @response_string = string ++ @response_array = cookies_from_response + end + + def request_cookies +diff --git a/sinatra-contrib/spec/cookies_spec.rb b/sinatra-contrib/spec/cookies_spec.rb +index bab46365..a87a6b36 100644 +--- a/sinatra-contrib/spec/cookies_spec.rb ++++ b/sinatra-contrib/spec/cookies_spec.rb +@@ -153,12 +153,12 @@ RSpec.describe Sinatra::Cookies do + expect(jar['foo']).to be_nil + end + +- it 'removes response cookies from cookies hash' do ++ it 'does not remove response cookies from cookies hash' do + expect(cookie_route do + cookies['foo'] = 'bar' + cookies.clear + cookies['foo'] +- end).to be_nil ++ end).to eq('bar') + end + + it 'expires existing cookies' do +@@ -189,12 +189,12 @@ RSpec.describe Sinatra::Cookies do + expect(jar['foo']).to be_nil + end + +- it 'removes response cookies from cookies hash' do ++ it 'does not remove response cookies from cookies hash' do + expect(cookie_route do + cookies['foo'] = 'bar' + cookies.delete 'foo' + cookies['foo'] +- end).to be_nil ++ end).to eq('bar') + end + + it 'expires existing cookies' do +@@ -246,13 +246,16 @@ RSpec.describe Sinatra::Cookies do + end + + describe :delete_if do +- it 'deletes cookies that match the block' do ++ it 'expires cookies that match the block' do + expect(cookie_route('foo=bar') do + cookies['bar'] = 'baz' + cookies['baz'] = 'foo' + cookies.delete_if { |*a| a.include? 'bar' } +- cookies.values_at 'foo', 'bar', 'baz' +- end).to eq([nil, nil, 'foo']) ++ response['Set-Cookie'] ++ end).to eq(["bar=baz; domain=example.org; path=/; httponly", ++ "baz=foo; domain=example.org; path=/; httponly", ++ "foo=; domain=example.org; path=/; max-age=0; expires=Thu, 01 Jan 1970 00:00:00 GMT; httponly", ++ "bar=; domain=example.org; path=/; max-age=0; expires=Thu, 01 Jan 1970 00:00:00 GMT; httponly"]) + end + end + +@@ -454,12 +457,12 @@ RSpec.describe Sinatra::Cookies do + end).to be false + end + +- it 'becomes true if response cookies are removed' do ++ it 'deos not become true if response cookies are removed' do + expect(cookie_route do + cookies['foo'] = 'bar' + cookies.delete :foo + cookies.empty? +- end).to be true ++ end).to be false + end + + it 'becomes true if request cookies are removed' do +@@ -469,12 +472,12 @@ RSpec.describe Sinatra::Cookies do + end).to be_truthy + end + +- it 'becomes true after clear' do ++ it 'does not become true after clear' do + expect(cookie_route('foo=bar', 'bar=baz') do + cookies['foo'] = 'bar' + cookies.clear + cookies.empty? +- end).to be_truthy ++ end).to be false + end + end + +diff --git a/sinatra-contrib/spec/link_header_spec.rb b/sinatra-contrib/spec/link_header_spec.rb +index dfd07c8e..2bea382f 100644 +--- a/sinatra-contrib/spec/link_header_spec.rb ++++ b/sinatra-contrib/spec/link_header_spec.rb +@@ -30,7 +30,7 @@ RSpec.describe Sinatra::LinkHeader do + describe :link do + it "sets link headers" do + get '/' +- expect(headers['Link'].lines).to include('; rel="something"') ++ expect(headers['Link']).to include('; rel="something"') + end + + it "returns link html tags" do +-- +2.39.2 + + +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Patrik Ragnarsson +Date: Fri, 30 Dec 2022 13:13:48 +0100 +Subject: [PATCH 13/19] Fix server registration + +Co-authored-by: Samuel Williams +--- + test/server_test.rb | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/test/server_test.rb b/test/server_test.rb +index 764295b8..c85a92a3 100644 +--- a/test/server_test.rb ++++ b/test/server_test.rb +@@ -24,7 +24,7 @@ module Rack::Handler + end + end + +- register 'mock', 'Rack::Handler::Mock' ++ register :mock, Mock + end + + class ServerTest < Minitest::Test +-- +2.39.2 + + +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Patrik Ragnarsson +Date: Fri, 30 Dec 2022 20:39:37 +0100 +Subject: [PATCH 14/19] Remove mongrel, time to let go of the past + +--- + examples/stream.ru | 1 - + lib/sinatra/base.rb | 3 +-- + 2 files changed, 1 insertion(+), 3 deletions(-) + +diff --git a/examples/stream.ru b/examples/stream.ru +index ee1615d6..272236c6 100644 +--- a/examples/stream.ru ++++ b/examples/stream.ru +@@ -4,7 +4,6 @@ + # + # run *one* of these: + # +-# rackup -s mongrel stream.ru # gem install mongrel + # unicorn stream.ru # gem install unicorn + # puma stream.ru # gem install puma + # rainbows -c rainbows.conf stream.ru # gem install rainbows eventmachine +diff --git a/lib/sinatra/base.rb b/lib/sinatra/base.rb +index 4914cbd5..15455624 100644 +--- a/lib/sinatra/base.rb ++++ b/lib/sinatra/base.rb +@@ -1520,7 +1520,7 @@ module Sinatra + alias stop! quit! + + # Run the Sinatra app as a self-hosted server using +- # Puma, Falcon, Mongrel, or WEBrick (in that order). If given a block, will call ++ # Puma, Falcon or WEBrick (in that order). If given a block, will call + # with the constructed handler once we have taken the stage. + def run!(options = {}, &block) + return if running? +@@ -1875,7 +1875,6 @@ module Sinatra + + server.unshift 'puma' + server.unshift 'falcon' if ruby_engine != 'jruby' +- server.unshift 'mongrel' if ruby_engine.nil? + server.unshift 'thin' if ruby_engine != 'jruby' + server.unshift 'trinidad' if ruby_engine == 'jruby' + +-- +2.39.2 + + +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Patrik Ragnarsson +Date: Mon, 13 Feb 2023 20:45:27 +0100 +Subject: [PATCH 15/19] Restore `continue-on-error` + +Revert "Run tests for sinatra-contrib and rack-protection" + +This reverts commit 13b5b0f9c375fbfe6afdb3e19533fbf5bb1dbc32. +--- + .github/workflows/test.yml | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml +index 57e7e3da..10642939 100644 +--- a/.github/workflows/test.yml ++++ b/.github/workflows/test.yml +@@ -67,12 +67,12 @@ jobs: + bundler-cache: true + + - name: Run sinatra tests +- continue-on-error: true ++ continue-on-error: ${{ matrix.allow-failure || false }} + id: tests + run: bundle exec rake + + - name: Run sinatra-contrib tests +- continue-on-error: true ++ continue-on-error: ${{ matrix.allow-failure || false }} + id: contrib-tests + working-directory: sinatra-contrib + run: | +-- +2.39.2 + + +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Patrik Ragnarsson +Date: Fri, 24 Feb 2023 18:25:00 +0100 +Subject: [PATCH 16/19] Depend on `rackup` and `rack-session` >=2.0.0 + +The 2.0 series of these games are the same as the 0.x series, as these +gems were created for Rack 3. +The 1.x series of these games are for Rack 2. +--- + sinatra.gemspec | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/sinatra.gemspec b/sinatra.gemspec +index 956ecde3..3211cda9 100644 +--- a/sinatra.gemspec ++++ b/sinatra.gemspec +@@ -47,8 +47,8 @@ RubyGems 2.0 or newer is required to protect against public gem pushes. You can + + s.add_dependency 'mustermann', '~> 3.0' + s.add_dependency 'rack', '>= 3.0.0.beta1', '< 4' +- s.add_dependency 'rackup', '>= 0.2.3', '< 1' +- s.add_dependency 'rack-session', '>= 0.3.0', '< 1' ++ s.add_dependency 'rackup', '>= 2.0.0', '< 3' ++ s.add_dependency 'rack-session', '>= 2.0.0', '< 3' + s.add_dependency 'rack-protection', version + s.add_dependency 'tilt', '~> 2.0' + +-- +2.39.2 + + +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Patrik Ragnarsson +Date: Fri, 24 Feb 2023 18:33:27 +0100 +Subject: [PATCH 17/19] `Rack::File` -> `Rack::Files` again + +Rebase gone wrong. + +Remove the old comment from 1dfae3d87e236785cade93b7b2d1adbd4b450eaa. +--- + lib/sinatra/base.rb | 4 +--- + 1 file changed, 1 insertion(+), 3 deletions(-) + +diff --git a/lib/sinatra/base.rb b/lib/sinatra/base.rb +index 15455624..5a7b3288 100644 +--- a/lib/sinatra/base.rb ++++ b/lib/sinatra/base.rb +@@ -289,9 +289,7 @@ module Sinatra + def block.each; yield(call) end + response.body = block + elsif value +- # Rack 2.0 returns a Rack::File::Iterator here instead of +- # Rack::File as it was in the previous API. +- unless request.head? || value.is_a?(Rack::File::Iterator) || value.is_a?(Stream) ++ unless request.head? || value.is_a?(Rack::Files::Iterator) || value.is_a?(Stream) + headers.delete 'content-length' + end + response.body = value +-- +2.39.2 + + +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Patrik Ragnarsson +Date: Sat, 25 Feb 2023 19:29:27 +0100 +Subject: [PATCH 18/19] Support `text/javascript` as JavaScript MIME type + +Next Rack release (probably 3.1) will include this change: + +https://github.com/rack/rack/commit/1bd0f1597d8f4a90d47115f3e156a8ce7870c9c8 +https://github.com/sinatra/sinatra/pull/1857#issuecomment-1445062212 +--- + lib/sinatra/base.rb | 8 +++++++- + sinatra-contrib/spec/json_spec.rb | 5 ++++- + test/helpers_test.rb | 4 +++- + 3 files changed, 14 insertions(+), 3 deletions(-) + +diff --git a/lib/sinatra/base.rb b/lib/sinatra/base.rb +index 5a7b3288..813fe017 100644 +--- a/lib/sinatra/base.rb ++++ b/lib/sinatra/base.rb +@@ -1402,7 +1402,13 @@ module Sinatra + # mime_types :js # => ['application/javascript', 'text/javascript'] + def mime_types(type) + type = mime_type type +- type =~ %r{^application/(xml|javascript)$} ? [type, "text/#{$1}"] : [type] ++ if type =~ %r{^application/(xml|javascript)$} ++ [type, "text/#{$1}"] ++ elsif type =~ %r{^text/(xml|javascript)$} ++ [type, "application/#{$1}"] ++ else ++ [type] ++ end + end + + # Define a before filter; runs before all requests within the same +diff --git a/sinatra-contrib/spec/json_spec.rb b/sinatra-contrib/spec/json_spec.rb +index ec76c42f..047cdda0 100644 +--- a/sinatra-contrib/spec/json_spec.rb ++++ b/sinatra-contrib/spec/json_spec.rb +@@ -57,7 +57,10 @@ RSpec.describe Sinatra::JSON do + + it "accepts shorthands for :content_type" do + mock_app { get('/') { json({}, :content_type => :js) } } +- expect(get('/')["Content-Type"]).to eq("application/javascript;charset=utf-8") ++ # Changed to "text/javascript" in Rack >3.0 ++ # https://github.com/sinatra/sinatra/pull/1857#issuecomment-1445062212 ++ expect(get('/')["Content-Type"]) ++ .to eq("application/javascript;charset=utf-8").or eq("text/javascript;charset=utf-8") + end + + it 'calls generate on :encoder if available' do +diff --git a/test/helpers_test.rb b/test/helpers_test.rb +index 04117610..a856bb91 100644 +--- a/test/helpers_test.rb ++++ b/test/helpers_test.rb +@@ -681,11 +681,13 @@ class HelpersTest < Minitest::Test + assert_equal content_type(:foo), 'text/foo;charset=utf-8' + assert_equal content_type(:xml), 'application/xml;charset=utf-8' + assert_equal content_type(:xhtml), 'application/xhtml+xml;charset=utf-8' +- assert_equal content_type(:js), 'application/javascript;charset=utf-8' + assert_equal content_type(:json), 'application/json' + assert_equal content_type(:bar), 'application/bar' + assert_equal content_type(:png), 'image/png' + assert_equal content_type(:baz), 'application/baz;charset=utf-8' ++ # Changed to "text/javascript" in Rack >3.0 ++ # https://github.com/sinatra/sinatra/pull/1857#issuecomment-1445062212 ++ assert_match %r{^application|text/javascript;charset=utf-8$}, content_type(:js) + tests_ran = true + "done" + end +-- +2.39.2 + + +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Patrik Ragnarsson +Date: Sat, 4 Mar 2023 23:17:55 +0100 +Subject: [PATCH 19/19] `Rack::Handler` -> `Rackup::Handler` + +--- + lib/sinatra/base.rb | 3 ++- + test/integration/rainbows.rb | 4 ++-- + test/server_test.rb | 2 +- + 3 files changed, 5 insertions(+), 4 deletions(-) + +diff --git a/lib/sinatra/base.rb b/lib/sinatra/base.rb +index 813fe017..eafa64b5 100644 +--- a/lib/sinatra/base.rb ++++ b/lib/sinatra/base.rb +@@ -2,6 +2,7 @@ + + # external dependencies + require 'rack' ++require 'rackup' + require 'tilt' + require 'rack/protection' + require 'mustermann' +@@ -1530,7 +1531,7 @@ module Sinatra + return if running? + + set options +- handler = Rack::Handler.pick(server) ++ handler = Rackup::Handler.pick(server) + handler_name = handler.name.gsub(/.*::/, '') + server_settings = settings.respond_to?(:server_settings) ? settings.server_settings : {} + server_settings.merge!(Port: port, Host: bind) +diff --git a/test/integration/rainbows.rb b/test/integration/rainbows.rb +index 895e19a2..004e54a9 100644 +--- a/test/integration/rainbows.rb ++++ b/test/integration/rainbows.rb +@@ -1,6 +1,6 @@ + require 'rainbows' + +-module Rack ++module Rackup + module Handler + class Rainbows + def self.run(app, **options) +@@ -15,6 +15,6 @@ module Rack + end + end + +- register :rainbows, ::Rack::Handler::Rainbows ++ register :rainbows, ::Rackup::Handler::Rainbows + end + end +diff --git a/test/server_test.rb b/test/server_test.rb +index c85a92a3..be5e51df 100644 +--- a/test/server_test.rb ++++ b/test/server_test.rb +@@ -1,7 +1,7 @@ + require_relative 'test_helper' + require 'stringio' + +-module Rack::Handler ++module Rackup::Handler + class Mock + extend Minitest::Assertions + # Allow assertions in request context +-- +2.39.2 +