rack-sanitizer 2.0.3 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/bin/rubocop ADDED
@@ -0,0 +1,27 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ #
5
+ # This file was generated by Bundler.
6
+ #
7
+ # The application 'rubocop' is installed as part of a gem, and
8
+ # this file is here to facilitate running it.
9
+ #
10
+
11
+ ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__)
12
+
13
+ bundle_binstub = File.expand_path("bundle", __dir__)
14
+
15
+ if File.file?(bundle_binstub)
16
+ if File.read(bundle_binstub, 300).include?("This file was generated by Bundler")
17
+ load(bundle_binstub)
18
+ else
19
+ abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
20
+ Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
21
+ end
22
+ end
23
+
24
+ require "rubygems"
25
+ require "bundler/setup"
26
+
27
+ load Gem.bin_path("rubocop", "rubocop")
data/dev.yml ADDED
@@ -0,0 +1,17 @@
1
+ name: rack-sanitizer
2
+
3
+ type: ruby
4
+ nix: true
5
+
6
+ up:
7
+ - ruby
8
+ - bundler
9
+
10
+ commands:
11
+ test:
12
+ syntax:
13
+ argument: file
14
+ optional: args...
15
+ run: bundle exec rake spec
16
+ style:
17
+ run: bin/rubocop
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gemspec path: ".."
6
+ gem "rubocop-shopify"
7
+ gem "rake"
8
+ gem "ostruct"
9
+ gem "rack", "~> 2.2.0"
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gemspec path: ".."
6
+ gem "rubocop-shopify"
7
+ gem "rake"
8
+ gem "ostruct"
9
+ gem "rack", "~> 3.0.0"
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gemspec path: ".."
6
+ gem "rubocop-shopify"
7
+ gem "rake"
8
+ gem "ostruct"
9
+ gem "rack", "~> 3.1.0"
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gemspec path: ".."
6
+ gem "rubocop-shopify"
7
+ gem "rake"
8
+ gem "ostruct"
9
+ gem "rack", "~> 3.2.0"
@@ -7,9 +7,11 @@ module Rack
7
7
  class Sanitizer
8
8
  # options[:sanitizable_content_types] Array
9
9
  # options[:additional_content_types] Array
10
- def initialize(app, options={})
10
+ def initialize(app, options = {})
11
11
  @app = app
12
- @strategy = build_strategy(options)
12
+ strategy = options.fetch(:strategy) { :replace }
13
+ @strategy = build_strategy(strategy)
14
+ @exception_strategy = strategy == :exception
13
15
  @sanitizable_content_types = options[:sanitizable_content_types]
14
16
  @sanitizable_content_types ||= SANITIZABLE_CONTENT_TYPES + (options[:additional_content_types] || [])
15
17
  end
@@ -17,25 +19,28 @@ module Rack
17
19
  def call(env)
18
20
  env = sanitize(env)
19
21
  begin
22
+ if @exception_strategy && env['rack.input'].is_a?(SanitizedRackInput)
23
+ env['rack.input'].ensure_sanitized
24
+ end
20
25
  @app.call(env)
21
26
  rescue SanitizedRackInput::FailedToReadBody
22
- return [400, { "Content-Type" => "text/plain" }, ["Bad Request"]]
27
+ [400, { "content-type" => "text/plain" }, ["Bad Request"]]
23
28
  end
24
29
  end
25
30
 
26
31
  DEFAULT_STRATEGIES = {
27
32
  replace: lambda do |input|
28
- input.
29
- force_encoding(Encoding::ASCII_8BIT).
30
- encode!(Encoding::UTF_8,
31
- invalid: :replace,
32
- undef: :replace)
33
+ input
34
+ .force_encoding(Encoding::ASCII_8BIT)
35
+ .encode!(Encoding::UTF_8,
36
+ invalid: :replace,
37
+ undef: :replace)
33
38
  input
34
39
  end,
35
40
  exception: lambda do |input|
36
- input.
37
- force_encoding(Encoding::ASCII_8BIT).
38
- encode!(Encoding::UTF_8)
41
+ input
42
+ .force_encoding(Encoding::ASCII_8BIT)
43
+ .encode!(Encoding::UTF_8)
39
44
  input
40
45
  end
41
46
  }.freeze
@@ -73,8 +78,6 @@ module Rack
73
78
  env[key] = sanitize_uri_encoded_string(value)
74
79
  end
75
80
  elsif key.to_s.start_with?("HTTP_")
76
- # Just sanitize the headers and leave them in UTF-8. There is
77
- # no reason to have UTF-8 in headers, but if it's valid, let it be.
78
81
  if value.frozen?
79
82
  env[key] = sanitize_string(value.dup).freeze
80
83
  else
@@ -86,9 +89,7 @@ module Rack
86
89
 
87
90
  private
88
91
 
89
- def build_strategy(options)
90
- strategy = options.fetch(:strategy) { :replace }
91
-
92
+ def build_strategy(strategy)
92
93
  return strategy unless DEFAULT_STRATEGIES.key?(strategy)
93
94
 
94
95
  DEFAULT_STRATEGIES[strategy]
@@ -128,7 +129,7 @@ module Rack
128
129
  return unless env['HTTP_COOKIE']
129
130
 
130
131
  env['HTTP_COOKIE'] = env['HTTP_COOKIE']
131
- .split(/[;,] */n)
132
+ .split(/; */n)
132
133
  .map { |cookie| sanitize_uri_encoded_string(cookie) }
133
134
  .join('; ')
134
135
  end
@@ -157,8 +158,8 @@ module Rack
157
158
 
158
159
  def decode_string(input)
159
160
  unescape_unreserved(
160
- sanitize_string(input).
161
- force_encoding(Encoding::ASCII_8BIT))
161
+ sanitize_string(input)
162
+ .force_encoding(Encoding::ASCII_8BIT))
162
163
  end
163
164
 
164
165
  # RFC3986, 2.2 states that the characters from 'reserved' group must be
@@ -196,16 +197,18 @@ module Rack
196
197
 
197
198
  def sanitize_string(input)
198
199
  if input.is_a? String
199
- input = input.force_encoding(Encoding::UTF_8)
200
+ # Handle chilled strings
201
+ # Rack values aren't supposed to be frozen, but it's common in test suites.
202
+ input = +input
200
203
 
201
- if input.valid_encoding?
202
- input
203
- else
204
- @strategy.call(input)
205
- end
206
- else
207
- input
204
+ input.force_encoding(Encoding::UTF_8)
205
+
206
+ input = @strategy.call(input) unless input.valid_encoding?
207
+ # Rack requires non-ASCII strings to use ASCII-8BIT encoding.
208
+ input = input.b if input.is_a?(String) && !input.ascii_only?
208
209
  end
210
+
211
+ input
209
212
  end
210
213
  end
211
214
 
@@ -236,6 +239,10 @@ module Rack
236
239
  sanitized_io.each(&block)
237
240
  end
238
241
 
242
+ def ensure_sanitized
243
+ sanitized_io
244
+ end
245
+
239
246
  def rewind
240
247
  sanitized_io.rewind
241
248
  end
@@ -1,27 +1,22 @@
1
- # -*- encoding: utf-8 -*-
1
+ # frozen_string_literal: true
2
2
 
3
3
  Gem::Specification.new do |gem|
4
4
  gem.name = "rack-sanitizer"
5
- gem.version = '2.0.3'
6
- gem.authors = ["Jean Boussier", "whitequark"]
5
+ gem.version = '2.1.0'
6
+ gem.authors = ["Shopify - Ruby and Rails Infratructure", "Jean Boussier", "whitequark"]
7
7
  gem.license = "MIT"
8
- gem.email = ["jean.boussier@gmail.org"]
9
- gem.description = %{Rack::Sanitizer is a Rack middleware which cleans up } <<
8
+ gem.email = ["ruby-and-rails-infrastructure@shopify.com"]
9
+ gem.description = %{Rack::Sanitizer is a Rack middleware which cleans up } +
10
10
  %{invalid UTF8 characters in request URI and headers.}
11
11
  gem.summary = "It is a mordernized and optimized fork of rack-utf8_sanitizer"
12
12
  gem.homepage = "http://github.com/Shopify/rack-sanitizer"
13
13
 
14
14
  gem.files = `git ls-files`.split($/)
15
- gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
16
15
  gem.require_paths = ["lib"]
17
16
 
18
17
  gem.metadata["allowed_push_host"] = "https://rubygems.org/"
19
18
 
20
19
  gem.required_ruby_version = '>= 2.5'
21
20
 
22
- gem.add_dependency "rack", '>= 1.0', '< 4.0'
23
-
24
- gem.add_development_dependency "bacon"
25
- gem.add_development_dependency "bacon-colored_output"
26
- gem.add_development_dependency "rake"
21
+ gem.add_dependency "rack", '>= 1.0', '< 4.0'
27
22
  end