Skip to content

Commit acdc3ae

Browse files
authored
Merge pull request from GHSA-48w2-rm65-62xx
* Fix HTTP request smuggling vulnerability See GHSA-48w2-rm65-62xx or CVE-2021-41136 for more info. * 4.3.9 release note * 5.5.1 release note * 5.5.1
1 parent 61dd7f4 commit acdc3ae

File tree

6 files changed

+95
-48
lines changed

6 files changed

+95
-48
lines changed

History.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
## 5.5.1 / 2021-10-12
2+
3+
* Security
4+
* Do not allow LF as a line ending in a header (CVE-2021-41136)
5+
16
## 5.5.0 / 2021-09-19
27

38
* Features
@@ -251,6 +256,11 @@
251256
* Support parallel tests in verbose progress reporting ([#2223])
252257
* Refactor error handling in server accept loop ([#2239])
253258

259+
## 4.3.9 / 2021-10-12
260+
261+
* Security
262+
* Do not allow LF as a line ending in a header (CVE-2021-41136)
263+
254264
## 4.3.8 / 2021-05-11
255265

256266
* Security

ext/puma_http11/http11_parser.c

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -426,10 +426,13 @@ case 17:
426426
case 18:
427427
#line 428 "ext/puma_http11/http11_parser.c"
428428
switch( (*p) ) {
429+
case 9: goto tr25;
429430
case 13: goto tr26;
430431
case 32: goto tr27;
431432
}
432-
goto tr25;
433+
if ( 33 <= (*p) && (*p) <= 126 )
434+
goto tr25;
435+
goto st0;
433436
tr25:
434437
#line 46 "ext/puma_http11/http11_parser.rl"
435438
{ MARK(mark, p); }
@@ -438,10 +441,14 @@ case 18:
438441
if ( ++p == pe )
439442
goto _test_eof19;
440443
case 19:
441-
#line 442 "ext/puma_http11/http11_parser.c"
442-
if ( (*p) == 13 )
443-
goto tr29;
444-
goto st19;
444+
#line 445 "ext/puma_http11/http11_parser.c"
445+
switch( (*p) ) {
446+
case 9: goto st19;
447+
case 13: goto tr29;
448+
}
449+
if ( 32 <= (*p) && (*p) <= 126 )
450+
goto st19;
451+
goto st0;
445452
tr9:
446453
#line 53 "ext/puma_http11/http11_parser.rl"
447454
{
@@ -484,7 +491,7 @@ case 19:
484491
if ( ++p == pe )
485492
goto _test_eof20;
486493
case 20:
487-
#line 488 "ext/puma_http11/http11_parser.c"
494+
#line 495 "ext/puma_http11/http11_parser.c"
488495
switch( (*p) ) {
489496
case 32: goto tr31;
490497
case 60: goto st0;
@@ -505,7 +512,7 @@ case 20:
505512
if ( ++p == pe )
506513
goto _test_eof21;
507514
case 21:
508-
#line 509 "ext/puma_http11/http11_parser.c"
515+
#line 516 "ext/puma_http11/http11_parser.c"
509516
switch( (*p) ) {
510517
case 32: goto tr33;
511518
case 60: goto st0;
@@ -526,7 +533,7 @@ case 21:
526533
if ( ++p == pe )
527534
goto _test_eof22;
528535
case 22:
529-
#line 530 "ext/puma_http11/http11_parser.c"
536+
#line 537 "ext/puma_http11/http11_parser.c"
530537
switch( (*p) ) {
531538
case 43: goto st22;
532539
case 58: goto st23;
@@ -551,7 +558,7 @@ case 22:
551558
if ( ++p == pe )
552559
goto _test_eof23;
553560
case 23:
554-
#line 555 "ext/puma_http11/http11_parser.c"
561+
#line 562 "ext/puma_http11/http11_parser.c"
555562
switch( (*p) ) {
556563
case 32: goto tr8;
557564
case 34: goto st0;
@@ -571,7 +578,7 @@ case 23:
571578
if ( ++p == pe )
572579
goto _test_eof24;
573580
case 24:
574-
#line 575 "ext/puma_http11/http11_parser.c"
581+
#line 582 "ext/puma_http11/http11_parser.c"
575582
switch( (*p) ) {
576583
case 32: goto tr37;
577584
case 34: goto st0;
@@ -594,7 +601,7 @@ case 24:
594601
if ( ++p == pe )
595602
goto _test_eof25;
596603
case 25:
597-
#line 598 "ext/puma_http11/http11_parser.c"
604+
#line 605 "ext/puma_http11/http11_parser.c"
598605
switch( (*p) ) {
599606
case 32: goto tr41;
600607
case 34: goto st0;
@@ -614,7 +621,7 @@ case 25:
614621
if ( ++p == pe )
615622
goto _test_eof26;
616623
case 26:
617-
#line 618 "ext/puma_http11/http11_parser.c"
624+
#line 625 "ext/puma_http11/http11_parser.c"
618625
switch( (*p) ) {
619626
case 32: goto tr44;
620627
case 34: goto st0;

ext/puma_http11/http11_parser_common.rl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@
4343

4444
field_name = ( token -- ":" )+ >start_field $snake_upcase_field %write_field;
4545

46-
field_value = any* >start_value %write_value;
46+
field_value = ( print | "\t" )* >start_value %write_value;
4747

4848
message_header = field_name ":" " "* field_value :> CRLF;
4949

ext/puma_http11/org/jruby/puma/Http11Parser.java

Lines changed: 34 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,9 @@ private static short[] init__puma_parser_key_offsets_0()
3434
{
3535
return new short [] {
3636
0, 0, 8, 17, 27, 29, 30, 31, 32, 33, 34, 36,
37-
39, 41, 44, 45, 61, 62, 78, 80, 81, 89, 97, 107,
38-
115, 124, 132, 140, 149, 158, 167, 176, 185, 194, 203, 212,
39-
221, 230, 239, 248, 257, 266, 275, 284, 293, 302, 303
37+
39, 41, 44, 45, 61, 62, 78, 83, 87, 95, 103, 113,
38+
121, 130, 138, 146, 155, 164, 173, 182, 191, 200, 209, 218,
39+
227, 236, 245, 254, 263, 272, 281, 290, 299, 308, 309
4040
};
4141
}
4242

@@ -52,14 +52,13 @@ private static char[] init__puma_parser_trans_keys_0()
5252
46, 48, 57, 48, 57, 13, 48, 57, 10, 13, 33, 124,
5353
126, 35, 39, 42, 43, 45, 46, 48, 57, 65, 90, 94,
5454
122, 10, 33, 58, 124, 126, 35, 39, 42, 43, 45, 46,
55-
48, 57, 65, 90, 94, 122, 13, 32, 13, 32, 60, 62,
56-
127, 0, 31, 34, 35, 32, 60, 62, 127, 0, 31, 34,
57-
35, 43, 58, 45, 46, 48, 57, 65, 90, 97, 122, 32,
58-
34, 35, 60, 62, 127, 0, 31, 32, 34, 35, 60, 62,
59-
63, 127, 0, 31, 32, 34, 35, 60, 62, 127, 0, 31,
60-
32, 34, 35, 60, 62, 127, 0, 31, 32, 36, 95, 45,
61-
46, 48, 57, 65, 90, 32, 36, 95, 45, 46, 48, 57,
62-
65, 90, 32, 36, 95, 45, 46, 48, 57, 65, 90, 32,
55+
48, 57, 65, 90, 94, 122, 9, 13, 32, 33, 126, 9,
56+
13, 32, 126, 32, 60, 62, 127, 0, 31, 34, 35, 32,
57+
60, 62, 127, 0, 31, 34, 35, 43, 58, 45, 46, 48,
58+
57, 65, 90, 97, 122, 32, 34, 35, 60, 62, 127, 0,
59+
31, 32, 34, 35, 60, 62, 63, 127, 0, 31, 32, 34,
60+
35, 60, 62, 127, 0, 31, 32, 34, 35, 60, 62, 127,
61+
0, 31, 32, 36, 95, 45, 46, 48, 57, 65, 90, 32,
6362
36, 95, 45, 46, 48, 57, 65, 90, 32, 36, 95, 45,
6463
46, 48, 57, 65, 90, 32, 36, 95, 45, 46, 48, 57,
6564
65, 90, 32, 36, 95, 45, 46, 48, 57, 65, 90, 32,
@@ -71,7 +70,8 @@ private static char[] init__puma_parser_trans_keys_0()
7170
65, 90, 32, 36, 95, 45, 46, 48, 57, 65, 90, 32,
7271
36, 95, 45, 46, 48, 57, 65, 90, 32, 36, 95, 45,
7372
46, 48, 57, 65, 90, 32, 36, 95, 45, 46, 48, 57,
74-
65, 90, 32, 0
73+
65, 90, 32, 36, 95, 45, 46, 48, 57, 65, 90, 32,
74+
36, 95, 45, 46, 48, 57, 65, 90, 32, 0
7575
};
7676
}
7777

@@ -82,7 +82,7 @@ private static byte[] init__puma_parser_single_lengths_0()
8282
{
8383
return new byte [] {
8484
0, 2, 3, 4, 2, 1, 1, 1, 1, 1, 0, 1,
85-
0, 1, 1, 4, 1, 4, 2, 1, 4, 4, 2, 6,
85+
0, 1, 1, 4, 1, 4, 3, 2, 4, 4, 2, 6,
8686
7, 6, 6, 3, 3, 3, 3, 3, 3, 3, 3, 3,
8787
3, 3, 3, 3, 3, 3, 3, 3, 3, 1, 0
8888
};
@@ -95,7 +95,7 @@ private static byte[] init__puma_parser_range_lengths_0()
9595
{
9696
return new byte [] {
9797
0, 3, 3, 3, 0, 0, 0, 0, 0, 0, 1, 1,
98-
1, 1, 0, 6, 0, 6, 0, 0, 2, 2, 4, 1,
98+
1, 1, 0, 6, 0, 6, 1, 1, 2, 2, 4, 1,
9999
1, 1, 1, 3, 3, 3, 3, 3, 3, 3, 3, 3,
100100
3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0
101101
};
@@ -108,9 +108,9 @@ private static short[] init__puma_parser_index_offsets_0()
108108
{
109109
return new short [] {
110110
0, 0, 6, 13, 21, 24, 26, 28, 30, 32, 34, 36,
111-
39, 41, 44, 46, 57, 59, 70, 73, 75, 82, 89, 96,
112-
104, 113, 121, 129, 136, 143, 150, 157, 164, 171, 178, 185,
113-
192, 199, 206, 213, 220, 227, 234, 241, 248, 255, 257
111+
39, 41, 44, 46, 57, 59, 70, 75, 79, 86, 93, 100,
112+
108, 117, 125, 133, 140, 147, 154, 161, 168, 175, 182, 189,
113+
196, 203, 210, 217, 224, 231, 238, 245, 252, 259, 261
114114
};
115115
}
116116

@@ -125,23 +125,23 @@ private static byte[] init__puma_parser_indicies_0()
125125
10, 1, 11, 1, 12, 1, 13, 1, 14, 1, 15, 1,
126126
16, 15, 1, 17, 1, 18, 17, 1, 19, 1, 20, 21,
127127
21, 21, 21, 21, 21, 21, 21, 21, 1, 22, 1, 23,
128-
24, 23, 23, 23, 23, 23, 23, 23, 23, 1, 26, 27,
129-
25, 29, 28, 30, 1, 1, 1, 1, 1, 31, 32, 1,
130-
1, 1, 1, 1, 33, 34, 35, 34, 34, 34, 34, 1,
131-
8, 1, 9, 1, 1, 1, 1, 35, 36, 1, 38, 1,
132-
1, 39, 1, 1, 37, 40, 1, 42, 1, 1, 1, 1,
133-
41, 43, 1, 45, 1, 1, 1, 1, 44, 2, 46, 46,
134-
46, 46, 46, 1, 2, 47, 47, 47, 47, 47, 1, 2,
135-
48, 48, 48, 48, 48, 1, 2, 49, 49, 49, 49, 49,
136-
1, 2, 50, 50, 50, 50, 50, 1, 2, 51, 51, 51,
137-
51, 51, 1, 2, 52, 52, 52, 52, 52, 1, 2, 53,
138-
53, 53, 53, 53, 1, 2, 54, 54, 54, 54, 54, 1,
139-
2, 55, 55, 55, 55, 55, 1, 2, 56, 56, 56, 56,
140-
56, 1, 2, 57, 57, 57, 57, 57, 1, 2, 58, 58,
141-
58, 58, 58, 1, 2, 59, 59, 59, 59, 59, 1, 2,
142-
60, 60, 60, 60, 60, 1, 2, 61, 61, 61, 61, 61,
143-
1, 2, 62, 62, 62, 62, 62, 1, 2, 63, 63, 63,
144-
63, 63, 1, 2, 1, 1, 0
128+
24, 23, 23, 23, 23, 23, 23, 23, 23, 1, 25, 26,
129+
27, 25, 1, 28, 29, 28, 1, 30, 1, 1, 1, 1,
130+
1, 31, 32, 1, 1, 1, 1, 1, 33, 34, 35, 34,
131+
34, 34, 34, 1, 8, 1, 9, 1, 1, 1, 1, 35,
132+
36, 1, 38, 1, 1, 39, 1, 1, 37, 40, 1, 42,
133+
1, 1, 1, 1, 41, 43, 1, 45, 1, 1, 1, 1,
134+
44, 2, 46, 46, 46, 46, 46, 1, 2, 47, 47, 47,
135+
47, 47, 1, 2, 48, 48, 48, 48, 48, 1, 2, 49,
136+
49, 49, 49, 49, 1, 2, 50, 50, 50, 50, 50, 1,
137+
2, 51, 51, 51, 51, 51, 1, 2, 52, 52, 52, 52,
138+
52, 1, 2, 53, 53, 53, 53, 53, 1, 2, 54, 54,
139+
54, 54, 54, 1, 2, 55, 55, 55, 55, 55, 1, 2,
140+
56, 56, 56, 56, 56, 1, 2, 57, 57, 57, 57, 57,
141+
1, 2, 58, 58, 58, 58, 58, 1, 2, 59, 59, 59,
142+
59, 59, 1, 2, 60, 60, 60, 60, 60, 1, 2, 61,
143+
61, 61, 61, 61, 1, 2, 62, 62, 62, 62, 62, 1,
144+
2, 63, 63, 63, 63, 63, 1, 2, 1, 1, 0
145145
};
146146
}
147147

lib/puma/const.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ class UnsupportedOption < RuntimeError
100100
# too taxing on performance.
101101
module Const
102102

103-
PUMA_VERSION = VERSION = "5.5.0".freeze
103+
PUMA_VERSION = VERSION = "5.5.1".freeze
104104
CODE_NAME = "Zawgyi".freeze
105105

106106
PUMA_SERVER_STRING = ['puma', PUMA_VERSION, CODE_NAME].join(' ').freeze

test/test_http11.rb

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,4 +208,34 @@ def test_trims_whitespace_from_headers
208208

209209
assert_equal "Strip This", req["HTTP_X_STRIP_ME"]
210210
end
211+
212+
def test_newline_smuggler
213+
parser = Puma::HttpParser.new
214+
req = {}
215+
http = "GET / HTTP/1.1\r\nHost: localhost:8080\r\nDummy: x\nDummy2: y\r\n\r\n"
216+
217+
parser.execute(req, http, 0) rescue nil # We test the raise elsewhere.
218+
219+
assert parser.error?, "Parser SHOULD have error"
220+
end
221+
222+
def test_newline_smuggler_two
223+
parser = Puma::HttpParser.new
224+
req = {}
225+
http = "GET / HTTP/1.1\r\nHost: localhost:8080\r\nDummy: x\r\nDummy: y\nDummy2: z\r\n\r\n"
226+
227+
parser.execute(req, http, 0) rescue nil
228+
229+
assert parser.error?, "Parser SHOULD have error"
230+
end
231+
232+
def test_htab_in_header_val
233+
parser = Puma::HttpParser.new
234+
req = {}
235+
http = "GET / HTTP/1.1\r\nHost: localhost:8080\r\nDummy: Valid\tValue\r\n\r\n"
236+
237+
parser.execute(req, http, 0)
238+
239+
assert_equal "Valid\tValue", req['HTTP_DUMMY']
240+
end
211241
end

0 commit comments

Comments
 (0)