puma 4.3.1 → 4.3.7

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of puma might be problematic. Click here for more details.

checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0c3e9fdfe5225baf88ff5ee9ec6f58201a220a482d72e5217309964a22b7ccc0
4
- data.tar.gz: 541ad3a7311662ca31e3de234870b03d6aac3eb925c8ad04e7a857bac097edc8
3
+ metadata.gz: 13b795e06976ed8b1004a190e018db051811b3518acdbe3679d0490fee7ee730
4
+ data.tar.gz: ebef22a424d8e8bc59dd8647b0f728818b9d1ae788ca9ff64a778830e3030349
5
5
  SHA512:
6
- metadata.gz: 8dc3a1d604212819aa8dc549b767cf00e3c237c95f0053ec61f6750666097980a1fbd2c350b1c47e59a1047d1a0bb35451914629f2829a66ded20a930939a60e
7
- data.tar.gz: f906230de96dd55841faaf6968939b4a096acb9933e91b34c8aada201e1c5e259c567ce8dce744490093072f6cb9d9553b7d83752e07ec7502973c72990563d4
6
+ metadata.gz: 98039dfcc8dac9fe7b7ac72cf9970649922c20b5510606a71d3078b83fb20ec8e83febe6be0a9d76bc9d01461937fe7bfd699622f5155e71e86b045fbbcc8dd9
7
+ data.tar.gz: bb502e59009a55d465b1903cc08f68e9841fbc832bda6f790bd46c7748f2684d5a1e9fc319225e3e49f073478ca2e270f6b5f3a37cc6e562846d84132796e164
data/History.md CHANGED
@@ -1,10 +1,29 @@
1
- ## Master
1
+ ## 4.3.7 / 2020-11-30
2
2
 
3
- * Features
4
- * Your feature goes here (#Github Number)
3
+ * Bugfixes
4
+ * Backport set CONTENT_LENGTH for chunked requests (Originally: #2287, backport: #2496)
5
+
6
+ ## 4.3.6 / 2020-09-05
5
7
 
6
8
  * Bugfixes
7
- * Your bugfix goes here (#Github Number)
9
+ * Explicitly include ctype.h to fix compilation warning and build error on macOS with Xcode 12 (#2304)
10
+ * Don't require json at boot (#2269)
11
+ * Set `CONTENT_LENGTH` for chunked requests (#2287)
12
+
13
+ ## 4.3.4/4.3.5 and 3.12.5/3.12.6 / 2020-05-22
14
+
15
+ Each patchlevel release contains a separate security fix. We recommend simply upgrading to 4.3.5/3.12.6.
16
+
17
+ ## 4.3.3 and 3.12.4 / 2020-02-28
18
+ * Bugfixes
19
+ * Fix: Fixes a problem where we weren't splitting headers correctly on newlines (#2132)
20
+ * Security
21
+ * Fix: Prevent HTTP Response splitting via CR in early hints.
22
+
23
+ ## 4.3.2 and 3.12.3 / 2020-02-27
24
+
25
+ * Security
26
+ * Fix: Prevent HTTP Response splitting via CR/LF in header values. CVE-2020-5247.
8
27
 
9
28
  ## 4.3.1 and 3.12.2 / 2019-12-05
10
29
 
@@ -1,7 +1,7 @@
1
1
  require 'mkmf'
2
2
 
3
3
  dir_config("puma_http11")
4
- if RUBY_PLATFORM[/mingw32/]
4
+ if $mingw && RUBY_VERSION >= '2.4'
5
5
  append_cflags '-D_FORTIFY_SOURCE=2'
6
6
  append_ldflags '-fstack-protector'
7
7
  have_library 'ssp'
@@ -14,12 +14,14 @@
14
14
 
15
15
  /*
16
16
  * capitalizes all lower-case ASCII characters,
17
- * converts dashes to underscores.
17
+ * converts dashes to underscores, and underscores to commas.
18
18
  */
19
19
  static void snake_upcase_char(char *c)
20
20
  {
21
21
  if (*c >= 'a' && *c <= 'z')
22
22
  *c &= ~0x20;
23
+ else if (*c == '_')
24
+ *c = ',';
23
25
  else if (*c == '-')
24
26
  *c = '_';
25
27
  }
@@ -12,12 +12,14 @@
12
12
 
13
13
  /*
14
14
  * capitalizes all lower-case ASCII characters,
15
- * converts dashes to underscores.
15
+ * converts dashes to underscores, and underscores to commas.
16
16
  */
17
17
  static void snake_upcase_char(char *c)
18
18
  {
19
19
  if (*c >= 'a' && *c <= 'z')
20
20
  *c &= ~0x20;
21
+ else if (*c == '_')
22
+ *c = ',';
21
23
  else if (*c == '-')
22
24
  *c = '_';
23
25
  }
@@ -10,6 +10,7 @@
10
10
  #include "ext_help.h"
11
11
  #include <assert.h>
12
12
  #include <string.h>
13
+ #include <ctype.h>
13
14
  #include "http11_parser.h"
14
15
 
15
16
  #ifndef MANAGED_STRINGS
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'json'
4
-
5
3
  module Puma
6
4
  module App
7
5
  # Check out {#call}'s source code to see what actions this web application
@@ -19,6 +17,10 @@ module Puma
19
17
  return rack_response(403, 'Invalid auth token', 'text/plain')
20
18
  end
21
19
 
20
+ if env['PATH_INFO'] =~ /\/(gc-stats|stats|thread-backtraces)$/
21
+ require 'json'
22
+ end
23
+
22
24
  case env['PATH_INFO']
23
25
  when /\/stop$/
24
26
  @cli.stop
@@ -153,7 +153,7 @@ module Puma
153
153
 
154
154
  begin
155
155
  data = @io.read_nonblock(CHUNK_SIZE)
156
- rescue Errno::EAGAIN
156
+ rescue IO::WaitReadable
157
157
  return false
158
158
  rescue SystemCallError, IOError, EOFError
159
159
  raise ConnectionError, "Connection error detected during read"
@@ -285,8 +285,16 @@ module Puma
285
285
 
286
286
  te = @env[TRANSFER_ENCODING2]
287
287
 
288
- if te && CHUNKED.casecmp(te) == 0
289
- return setup_chunked_body(body)
288
+ if te
289
+ if te.include?(",")
290
+ te.split(",").each do |part|
291
+ if CHUNKED.casecmp(part.strip) == 0
292
+ return setup_chunked_body(body)
293
+ end
294
+ end
295
+ elsif CHUNKED.casecmp(te) == 0
296
+ return setup_chunked_body(body)
297
+ end
290
298
  end
291
299
 
292
300
  @chunked_body = false
@@ -343,7 +351,7 @@ module Puma
343
351
 
344
352
  begin
345
353
  chunk = @io.read_nonblock(want)
346
- rescue Errno::EAGAIN
354
+ rescue IO::WaitReadable
347
355
  return false
348
356
  rescue SystemCallError, IOError
349
357
  raise ConnectionError, "Connection error detected during read"
@@ -389,7 +397,10 @@ module Puma
389
397
  raise EOFError
390
398
  end
391
399
 
392
- return true if decode_chunk(chunk)
400
+ if decode_chunk(chunk)
401
+ @env[CONTENT_LENGTH] = @chunked_content_length
402
+ return true
403
+ end
393
404
  end
394
405
  end
395
406
 
@@ -402,19 +413,28 @@ module Puma
402
413
  @body.binmode
403
414
  @tempfile = @body
404
415
 
405
- return decode_chunk(body)
416
+ @chunked_content_length = 0
417
+
418
+ if decode_chunk(body)
419
+ @env[CONTENT_LENGTH] = @chunked_content_length
420
+ return true
421
+ end
422
+ end
423
+
424
+ def write_chunk(str)
425
+ @chunked_content_length += @body.write(str)
406
426
  end
407
427
 
408
428
  def decode_chunk(chunk)
409
429
  if @partial_part_left > 0
410
430
  if @partial_part_left <= chunk.size
411
431
  if @partial_part_left > 2
412
- @body << chunk[0..(@partial_part_left-3)] # skip the \r\n
432
+ write_chunk(chunk[0..(@partial_part_left-3)]) # skip the \r\n
413
433
  end
414
434
  chunk = chunk[@partial_part_left..-1]
415
435
  @partial_part_left = 0
416
436
  else
417
- @body << chunk if @partial_part_left > 2 # don't include the last \r\n
437
+ write_chunk(chunk) if @partial_part_left > 2 # don't include the last \r\n
418
438
  @partial_part_left -= chunk.size
419
439
  return false
420
440
  end
@@ -461,12 +481,12 @@ module Puma
461
481
 
462
482
  case
463
483
  when got == len
464
- @body << part[0..-3] # to skip the ending \r\n
484
+ write_chunk(part[0..-3]) # to skip the ending \r\n
465
485
  when got <= len - 2
466
- @body << part
486
+ write_chunk(part)
467
487
  @partial_part_left = len - part.size
468
488
  when got == len - 1 # edge where we get just \r but not \n
469
- @body << part[0..-2]
489
+ write_chunk(part[0..-2])
470
490
  @partial_part_left = len - part.size
471
491
  end
472
492
  else
@@ -100,7 +100,7 @@ module Puma
100
100
  # too taxing on performance.
101
101
  module Const
102
102
 
103
- PUMA_VERSION = VERSION = "4.3.1".freeze
103
+ PUMA_VERSION = VERSION = "4.3.7".freeze
104
104
  CODE_NAME = "Mysterious Traveller".freeze
105
105
  PUMA_SERVER_STRING = ['puma', PUMA_VERSION, CODE_NAME].join(' ').freeze
106
106
 
@@ -228,6 +228,7 @@ module Puma
228
228
  COLON = ": ".freeze
229
229
 
230
230
  NEWLINE = "\n".freeze
231
+ HTTP_INJECTION_REGEX = /[\r\n]/.freeze
231
232
 
232
233
  HIJACK_P = "rack.hijack?".freeze
233
234
  HIJACK = "rack.hijack".freeze
@@ -657,6 +657,7 @@ module Puma
657
657
  headers.each_pair do |k, vs|
658
658
  if vs.respond_to?(:to_s) && !vs.to_s.empty?
659
659
  vs.to_s.split(NEWLINE).each do |v|
660
+ next if possible_header_injection?(v)
660
661
  fast_write client, "#{k}: #{v}\r\n"
661
662
  end
662
663
  else
@@ -671,6 +672,37 @@ module Puma
671
672
  }
672
673
  end
673
674
 
675
+ # Fixup any headers with , in the name to have _ now. We emit
676
+ # headers with , in them during the parse phase to avoid ambiguity
677
+ # with the - to _ conversion for critical headers. But here for
678
+ # compatibility, we'll convert them back. This code is written to
679
+ # avoid allocation in the common case (ie there are no headers
680
+ # with , in their names), that's why it has the extra conditionals.
681
+
682
+ to_delete = nil
683
+ to_add = nil
684
+
685
+ env.each do |k,v|
686
+ if k.start_with?("HTTP_") and k.include?(",") and k != "HTTP_TRANSFER,ENCODING"
687
+ if to_delete
688
+ to_delete << k
689
+ else
690
+ to_delete = [k]
691
+ end
692
+
693
+ unless to_add
694
+ to_add = {}
695
+ end
696
+
697
+ to_add[k.tr(",", "_")] = v
698
+ end
699
+ end
700
+
701
+ if to_delete
702
+ to_delete.each { |k| env.delete(k) }
703
+ env.merge! to_add
704
+ end
705
+
674
706
  # A rack extension. If the app writes #call'ables to this
675
707
  # array, we will invoke them when the request is done.
676
708
  #
@@ -758,6 +790,7 @@ module Puma
758
790
  headers.each do |k, vs|
759
791
  case k.downcase
760
792
  when CONTENT_LENGTH2
793
+ next if possible_header_injection?(vs)
761
794
  content_length = vs
762
795
  next
763
796
  when TRANSFER_ENCODING
@@ -770,6 +803,7 @@ module Puma
770
803
 
771
804
  if vs.respond_to?(:to_s) && !vs.to_s.empty?
772
805
  vs.to_s.split(NEWLINE).each do |v|
806
+ next if possible_header_injection?(v)
773
807
  lines.append k, colon, v, line_ending
774
808
  end
775
809
  else
@@ -1040,5 +1074,10 @@ module Puma
1040
1074
  def shutting_down?
1041
1075
  @status == :stop || @status == :restart
1042
1076
  end
1077
+
1078
+ def possible_header_injection?(header_value)
1079
+ HTTP_INJECTION_REGEX =~ header_value.to_s
1080
+ end
1081
+ private :possible_header_injection?
1043
1082
  end
1044
1083
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: puma
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.3.1
4
+ version: 4.3.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Evan Phoenix
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-12-05 00:00:00.000000000 Z
11
+ date: 2020-11-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: nio4r
@@ -121,7 +121,7 @@ licenses:
121
121
  metadata:
122
122
  msys2_mingw_dependencies: openssl
123
123
  changelog_uri: https://github.com/puma/puma/blob/master/History.md
124
- post_install_message:
124
+ post_install_message:
125
125
  rdoc_options: []
126
126
  require_paths:
127
127
  - lib
@@ -136,8 +136,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
136
136
  - !ruby/object:Gem::Version
137
137
  version: '0'
138
138
  requirements: []
139
- rubygems_version: 3.0.3
140
- signing_key:
139
+ rubygems_version: 3.1.4
140
+ signing_key:
141
141
  specification_version: 4
142
142
  summary: Puma is a simple, fast, threaded, and highly concurrent HTTP 1.1 server for
143
143
  Ruby/Rack applications