CVE-2021-41136: Inconsistent Interpretation of HTTP Requests ('HTTP Request/Response Smuggling') (CWE-444)
value: Puma is a HTTP 1.1 server for Ruby/Rack applications. Prior to versions 5.5.1 and 4.3.9, using `puma` with a proxy which forwards HTTP header values which contain the LF character could allow HTTP request smugggling. A client could smuggle a request through a proxy, causing the proxy to send a response back to another unknown client. The only proxy which has this behavior, as far as the Puma team is aware of, is Apache Traffic Server. If the proxy uses persistent connections and the client adds another request in via HTTP pipelining, the proxy may mistake it as the first request's body. Puma, however, would see it as two requests, and when processing the second request, send back a response that the proxy does not expect. If the proxy has reused the persistent connection to Puma to send another request for a different client, the second response from the first client will be sent to the second client. This vulnerability was patched in Puma 5.5.1 and 4.3.9. As a workaround, do not use Apache Traffic Server with `puma`.
CWE description: The product acts as an intermediary HTTP agent
         (such as a proxy or firewall) in the data flow between two
         entities such as a client and server, but it does not
         interpret malformed HTTP requests or responses in ways that
         are consistent with how the messages will be processed by
         those entities that are at the ultimate destination.
CVSS v3 base score: 3.7
Published: 2021-10-12T16:15Z
Repository: https://github.com/puma/puma
Language: Ruby
Files changed: History.md, ext/puma_http11/http11_parser.c, ext/puma_http11/http11_parser_common.rl, ext/puma_http11/org/jruby/puma/Http11Parser.java, lib/puma/const.rb, test/test_http11.rb
Commit date: 2021-10-12 08:38:40 -0600
Commit message: 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
Diff: diff --git a/History.md b/History.md
index be3d97d5..f2c72b3b 100644
--- a/History.md
+++ b/History.md
@@ -1,5 +1,10 @@
+## 5.5.1 / 2021-10-12
+
+* Security
+  * Do not allow LF as a line ending in a header (CVE-2021-41136)
+
 ## 5.5.0 / 2021-09-19
 
 * Features
   * Automatic SSL certificate provisioning for localhost, via localhost gem ([#2610], [#2257])
   * add support for the PROXY protocol (v1 only) ([#2654], [#2651])
@@ -249,10 +254,15 @@
   * ThreadPool concurrency refactoring ([#2220])
   * JSON parse cluster worker stats instead of regex ([#2124])
   * Support parallel tests in verbose progress reporting ([#2223])
   * Refactor error handling in server accept loop ([#2239])
 
+## 4.3.9 / 2021-10-12
+
+* Security
+  * Do not allow LF as a line ending in a header (CVE-2021-41136)
+
 ## 4.3.8 / 2021-05-11
 
 * Security
   * Close keepalive connections after the maximum number of fast inlined requests (CVE-2021-29509) ([#2625])
 
diff --git a/ext/puma_http11/http11_parser.c b/ext/puma_http11/http11_parser.c
index 2da0263e..6c571b34 100644
--- a/ext/puma_http11/http11_parser.c
+++ b/ext/puma_http11/http11_parser.c
@@ -424,26 +424,33 @@ st18:
 	if ( ++p == pe )
 		goto _test_eof18;
 case 18:
 #line 428 "ext/puma_http11/http11_parser.c"
 	switch( (*p) ) {
+		case 9: goto tr25;
 		case 13: goto tr26;
 		case 32: goto tr27;
 	}
-	goto tr25;
+	if ( 33 <= (*p) && (*p) <= 126 )
+		goto tr25;
+	goto st0;
 tr25:
 #line 46 "ext/puma_http11/http11_parser.rl"
 	{ MARK(mark, p); }
 	goto st19;
 st19:
 	if ( ++p == pe )
 		goto _test_eof19;
 case 19:
-#line 442 "ext/puma_http11/http11_parser.c"
-	if ( (*p) == 13 )
-		goto tr29;
-	goto st19;
+#line 445 "ext/puma_http11/http11_parser.c"
+	switch( (*p) ) {
+		case 9: goto st19;
+		case 13: goto tr29;
+	}
+	if ( 32 <= (*p) && (*p) <= 126 )
+		goto st19;
+	goto st0;
 tr9:
 #line 53 "ext/puma_http11/http11_parser.rl"
 	{
     parser->request_uri(parser, PTR_TO(mark), LEN(mark, p));
   }
@@ -482,11 +489,11 @@ tr45:
 	goto st20;
 st20:
 	if ( ++p == pe )
 		goto _test_eof20;
 case 20:
-#line 488 "ext/puma_http11/http11_parser.c"
+#line 495 "ext/puma_http11/http11_parser.c"
 	switch( (*p) ) {
 		case 32: goto tr31;
 		case 60: goto st0;
 		case 62: goto st0;
 		case 127: goto st0;
@@ -503,11 +510,11 @@ tr30:
 	goto st21;
 st21:
 	if ( ++p == pe )
 		goto _test_eof21;
 case 21:
-#line 509 "ext/puma_http11/http11_parser.c"
+#line 516 "ext/puma_http11/http11_parser.c"
 	switch( (*p) ) {
 		case 32: goto tr33;
 		case 60: goto st0;
 		case 62: goto st0;
 		case 127: goto st0;
@@ -524,11 +531,11 @@ tr5:
 	goto st22;
 st22:
 	if ( ++p == pe )
 		goto _test_eof22;
 case 22:
-#line 530 "ext/puma_http11/http11_parser.c"
+#line 537 "ext/puma_http11/http11_parser.c"
 	switch( (*p) ) {
 		case 43: goto st22;
 		case 58: goto st23;
 	}
 	if ( (*p) < 48 ) {
@@ -549,11 +556,11 @@ tr7:
 	goto st23;
 st23:
 	if ( ++p == pe )
 		goto _test_eof23;
 case 23:
-#line 555 "ext/puma_http11/http11_parser.c"
+#line 562 "ext/puma_http11/http11_parser.c"
 	switch( (*p) ) {
 		case 32: goto tr8;
 		case 34: goto st0;
 		case 35: goto tr9;
 		case 60: goto st0;
@@ -569,11 +576,11 @@ tr6:
 	goto st24;
 st24:
 	if ( ++p == pe )
 		goto _test_eof24;
 case 24:
-#line 575 "ext/puma_http11/http11_parser.c"
+#line 582 "ext/puma_http11/http11_parser.c"
 	switch( (*p) ) {
 		case 32: goto tr37;
 		case 34: goto st0;
 		case 35: goto tr38;
 		case 60: goto st0;
@@ -592,11 +599,11 @@ tr39:
 	goto st25;
 st25:
 	if ( ++p == pe )
 		goto _test_eof25;
 case 25:
-#line 598 "ext/puma_http11/http11_parser.c"
+#line 605 "ext/puma_http11/http11_parser.c"
 	switch( (*p) ) {
 		case 32: goto tr41;
 		case 34: goto st0;
 		case 35: goto tr42;
 		case 60: goto st0;
@@ -612,11 +619,11 @@ tr40:
 	goto st26;
 st26:
 	if ( ++p == pe )
 		goto _test_eof26;
 case 26:
-#line 618 "ext/puma_http11/http11_parser.c"
+#line 625 "ext/puma_http11/http11_parser.c"
 	switch( (*p) ) {
 		case 32: goto tr44;
 		case 34: goto st0;
 		case 35: goto tr45;
 		case 60: goto st0;
diff --git a/ext/puma_http11/http11_parser_common.rl b/ext/puma_http11/http11_parser_common.rl
index ba5f8a56..5eba09f2 100644
--- a/ext/puma_http11/http11_parser_common.rl
+++ b/ext/puma_http11/http11_parser_common.rl
@@ -41,11 +41,11 @@
   HTTP_Version = ( "HTTP/" http_number ) >mark %http_version ;
   Request_Line = ( Method " " Request_URI ("#" Fragment){0,1} " " HTTP_Version CRLF ) ;
 
   field_name = ( token -- ":" )+ >start_field $snake_upcase_field %write_field;
 
-  field_value = any* >start_value %write_value;
+  field_value = ( print | "\t" )* >start_value %write_value;
 
   message_header = field_name ":" " "* field_value :> CRLF;
 
   Request = Request_Line ( message_header )* ( CRLF @done );
 
diff --git a/ext/puma_http11/org/jruby/puma/Http11Parser.java b/ext/puma_http11/org/jruby/puma/Http11Parser.java
index 0bba664f..f5db1b69 100644
--- a/ext/puma_http11/org/jruby/puma/Http11Parser.java
+++ b/ext/puma_http11/org/jruby/puma/Http11Parser.java
@@ -32,13 +32,13 @@ private static final byte _puma_parser_actions[] = init__puma_parser_actions_0()
 
 private static short[] init__puma_parser_key_offsets_0()
 {
 	return new short [] {
 	    0,    0,    8,   17,   27,   29,   30,   31,   32,   33,   34,   36,
-	   39,   41,   44,   45,   61,   62,   78,   80,   81,   89,   97,  107,
-	  115,  124,  132,  140,  149,  158,  167,  176,  185,  194,  203,  212,
-	  221,  230,  239,  248,  257,  266,  275,  284,  293,  302,  303
+	   39,   41,   44,   45,   61,   62,   78,   83,   87,   95,  103,  113,
+	  121,  130,  138,  146,  155,  164,  173,  182,  191,  200,  209,  218,
+	  227,  236,  245,  254,  263,  272,  281,  290,  299,  308,  309
 	};
 }
 
 private static final short _puma_parser_key_offsets[] = init__puma_parser_key_offsets_0();
 
@@ -50,18 +50,17 @@ private static char[] init__puma_parser_trans_keys_0()
 	   46,   48,   57,   65,   90,   42,   43,   47,   58,   45,   57,   65,
 	   90,   97,  122,   32,   35,   72,   84,   84,   80,   47,   48,   57,
 	   46,   48,   57,   48,   57,   13,   48,   57,   10,   13,   33,  124,
 	  126,   35,   39,   42,   43,   45,   46,   48,   57,   65,   90,   94,
 	  122,   10,   33,   58,  124,  126,   35,   39,   42,   43,   45,   46,
-	   48,   57,   65,   90,   94,  122,   13,   32,   13,   32,   60,   62,
-	  127,    0,   31,   34,   35,   32,   60,   62,  127,    0,   31,   34,
-	   35,   43,   58,   45,   46,   48,   57,   65,   90,   97,  122,   32,
-	   34,   35,   60,   62,  127,    0,   31,   32,   34,   35,   60,   62,
-	   63,  127,    0,   31,   32,   34,   35,   60,   62,  127,    0,   31,
-	   32,   34,   35,   60,   62,  127,    0,   31,   32,   36,   95,   45,
-	   46,   48,   57,   65,   90,   32,   36,   95,   45,   46,   48,   57,
-	   65,   90,   32,   36,   95,   45,   46,   48,   57,   65,   90,   32,
+	   48,   57,   65,   90,   94,  122,    9,   13,   32,   33,  126,    9,
+	   13,   32,  126,   32,   60,   62,  127,    0,   31,   34,   35,   32,
+	   60,   62,  127,    0,   31,   34,   35,   43,   58,   45,   46,   48,
+	   57,   65,   90,   97,  122,   32,   34,   35,   60,   62,  127,    0,
+	   31,   32,   34,   35,   60,   62,   63,  127,    0,   31,   32,   34,
+	   35,   60,   62,  127,    0,   31,   32,   34,   35,   60,   62,  127,
+	    0,   31,   32,   36,   95,   45,   46,   48,   57,   65,   90,   32,
 	   36,   95,   45,   46,   48,   57,   65,   90,   32,   36,   95,   45,
 	   46,   48,   57,   65,   90,   32,   36,   95,   45,   46,   48,   57,
 	   65,   90,   32,   36,   95,   45,   46,   48,   57,   65,   90,   32,
 	   36,   95,   45,   46,   48,   57,   65,   90,   32,   36,   95,   45,
 	   46,   48,   57,   65,   90,   32,   36,   95,   45,   46,   48,   57,
@@ -69,22 +68,23 @@ private static char[] init__puma_parser_trans_keys_0()
 	   36,   95,   45,   46,   48,   57,   65,   90,   32,   36,   95,   45,
 	   46,   48,   57,   65,   90,   32,   36,   95,   45,   46,   48,   57,
 	   65,   90,   32,   36,   95,   45,   46,   48,   57,   65,   90,   32,
 	   36,   95,   45,   46,   48,   57,   65,   90,   32,   36,   95,   45,
 	   46,   48,   57,   65,   90,   32,   36,   95,   45,   46,   48,   57,
-	   65,   90,   32,    0
+	   65,   90,   32,   36,   95,   45,   46,   48,   57,   65,   90,   32,
+	   36,   95,   45,   46,   48,   57,   65,   90,   32,    0
 	};
 }
 
 private static final char _puma_parser_trans_keys[] = init__puma_parser_trans_keys_0();
 
 
 private static byte[] init__puma_parser_single_lengths_0()
 {
 	return new byte [] {
 	    0,    2,    3,    4,    2,    1,    1,    1,    1,    1,    0,    1,
-	    0,    1,    1,    4,    1,    4,    2,    1,    4,    4,    2,    6,
+	    0,    1,    1,    4,    1,    4,    3,    2,    4,    4,    2,    6,
 	    7,    6,    6,    3,    3,    3,    3,    3,    3,    3,    3,    3,
 	    3,    3,    3,    3,    3,    3,    3,    3,    3,    1,    0
 	};
 }
 
@@ -93,11 +93,11 @@ private static final byte _puma_parser_single_lengths[] = init__puma_parser_sing
 
 private static byte[] init__puma_parser_range_lengths_0()
 {
 	return new byte [] {
 	    0,    3,    3,    3,    0,    0,    0,    0,    0,    0,    1,    1,
-	    1,    1,    0,    6,    0,    6,    0,    0,    2,    2,    4,    1,
+	    1,    1,    0,    6,    0,    6,    1,    1,    2,    2,    4,    1,
 	    1,    1,    1,    3,    3,    3,    3,    3,    3,    3,    3,    3,
 	    3,    3,    3,    3,    3,    3,    3,    3,    3,    0,    0
 	};
 }
 
@@ -106,13 +106,13 @@ private static final byte _puma_parser_range_lengths[] = init__puma_parser_range
 
 private static short[] init__puma_parser_index_offsets_0()
 {
 	return new short [] {
 	    0,    0,    6,   13,   21,   24,   26,   28,   30,   32,   34,   36,
-	   39,   41,   44,   46,   57,   59,   70,   73,   75,   82,   89,   96,
-	  104,  113,  121,  129,  136,  143,  150,  157,  164,  171,  178,  185,
-	  192,  199,  206,  213,  220,  227,  234,  241,  248,  255,  257
+	   39,   41,   44,   46,   57,   59,   70,   75,   79,   86,   93,  100,
+	  108,  117,  125,  133,  140,  147,  154,  161,  168,  175,  182,  189,
+	  196,  203,  210,  217,  224,  231,  238,  245,  252,  259,  261
 	};
 }
 
 private static final short _puma_parser_index_offsets[] = init__puma_parser_index_offsets_0();
 
@@ -123,27 +123,27 @@ private static byte[] init__puma_parser_indicies_0()
 	    0,    0,    0,    0,    0,    1,    2,    3,    3,    3,    3,    3,
 	    1,    4,    5,    6,    7,    5,    5,    5,    1,    8,    9,    1,
 	   10,    1,   11,    1,   12,    1,   13,    1,   14,    1,   15,    1,
 	   16,   15,    1,   17,    1,   18,   17,    1,   19,    1,   20,   21,
 	   21,   21,   21,   21,   21,   21,   21,   21,    1,   22,    1,   23,
-	   24,   23,   23,   23,   23,   23,   23,   23,   23,    1,   26,   27,
-	   25,   29,   28,   30,    1,    1,    1,    1,    1,   31,   32,    1,
-	    1,    1,    1,    1,   33,   34,   35,   34,   34,   34,   34,    1,
-	    8,    1,    9,    1,    1,    1,    1,   35,   36,    1,   38,    1,
-	    1,   39,    1,    1,   37,   40,    1,   42,    1,    1,    1,    1,
-	   41,   43,    1,   45,    1,    1,    1,    1,   44,    2,   46,   46,
-	   46,   46,   46,    1,    2,   47,   47,   47,   47,   47,    1,    2,
-	   48,   48,   48,   48,   48,    1,    2,   49,   49,   49,   49,   49,
-	    1,    2,   50,   50,   50,   50,   50,    1,    2,   51,   51,   51,
-	   51,   51,    1,    2,   52,   52,   52,   52,   52,    1,    2,   53,
-	   53,   53,   53,   53,    1,    2,   54,   54,   54,   54,   54,    1,
-	    2,   55,   55,   55,   55,   55,    1,    2,   56,   56,   56,   56,
-	   56,    1,    2,   57,   57,   57,   57,   57,    1,    2,   58,   58,
-	   58,   58,   58,    1,    2,   59,   59,   59,   59,   59,    1,    2,
-	   60,   60,   60,   60,   60,    1,    2,   61,   61,   61,   61,   61,
-	    1,    2,   62,   62,   62,   62,   62,    1,    2,   63,   63,   63,
-	   63,   63,    1,    2,    1,    1,    0
+	   24,   23,   23,   23,   23,   23,   23,   23,   23,    1,   25,   26,
+	   27,   25,    1,   28,   29,   28,    1,   30,    1,    1,    1,    1,
+	    1,   31,   32,    1,    1,    1,    1,    1,   33,   34,   35,   34,
+	   34,   34,   34,    1,    8,    1,    9,    1,    1,    1,    1,   35,
+	   36,    1,   38,    1,    1,   39,    1,    1,   37,   40,    1,   42,
+	    1,    1,    1,    1,   41,   43,    1,   45,    1,    1,    1,    1,
+	   44,    2,   46,   46,   46,   46,   46,    1,    2,   47,   47,   47,
+	   47,   47,    1,    2,   48,   48,   48,   48,   48,    1,    2,   49,
+	   49,   49,   49,   49,    1,    2,   50,   50,   50,   50,   50,    1,
+	    2,   51,   51,   51,   51,   51,    1,    2,   52,   52,   52,   52,
+	   52,    1,    2,   53,   53,   53,   53,   53,    1,    2,   54,   54,
+	   54,   54,   54,    1,    2,   55,   55,   55,   55,   55,    1,    2,
+	   56,   56,   56,   56,   56,    1,    2,   57,   57,   57,   57,   57,
+	    1,    2,   58,   58,   58,   58,   58,    1,    2,   59,   59,   59,
+	   59,   59,    1,    2,   60,   60,   60,   60,   60,    1,    2,   61,
+	   61,   61,   61,   61,    1,    2,   62,   62,   62,   62,   62,    1,
+	    2,   63,   63,   63,   63,   63,    1,    2,    1,    1,    0
 	};
 }
 
 private static final byte _puma_parser_indicies[] = init__puma_parser_indicies_0();
 
diff --git a/lib/puma/const.rb b/lib/puma/const.rb
index dbe42b21..24528572 100644
--- a/lib/puma/const.rb
+++ b/lib/puma/const.rb
@@ -98,11 +98,11 @@ module Puma
   # While Puma does try to emulate the CGI/1.2 protocol, it does not use the REMOTE_IDENT,
   # REMOTE_USER, or REMOTE_HOST parameters since those are either a security problem or
   # too taxing on performance.
   module Const
 
-    PUMA_VERSION = VERSION = "5.5.0".freeze
+    PUMA_VERSION = VERSION = "5.5.1".freeze
     CODE_NAME = "Zawgyi".freeze
 
     PUMA_SERVER_STRING = ['puma', PUMA_VERSION, CODE_NAME].join(' ').freeze
 
     FAST_TRACK_KA_TIMEOUT = 0.2
diff --git a/test/test_http11.rb b/test/test_http11.rb
index 9e49b7b3..1d7f1158 100644
--- a/test/test_http11.rb
+++ b/test/test_http11.rb
@@ -206,6 +206,36 @@ class Http11ParserTest < Minitest::Test
 
     parser.execute(req, http, 0)
 
     assert_equal "Strip This", req["HTTP_X_STRIP_ME"]
   end
+
+  def test_newline_smuggler
+    parser = Puma::HttpParser.new
+    req = {}
+    http = "GET / HTTP/1.1\r\nHost: localhost:8080\r\nDummy: x\nDummy2: y\r\n\r\n"
+
+    parser.execute(req, http, 0) rescue nil # We test the raise elsewhere.
+
+    assert parser.error?, "Parser SHOULD have error"
+  end
+
+  def test_newline_smuggler_two
+    parser = Puma::HttpParser.new
+    req = {}
+    http = "GET / HTTP/1.1\r\nHost: localhost:8080\r\nDummy: x\r\nDummy: y\nDummy2: z\r\n\r\n"
+
+    parser.execute(req, http, 0) rescue nil
+
+    assert parser.error?, "Parser SHOULD have error"
+  end
+
+  def test_htab_in_header_val
+    parser = Puma::HttpParser.new
+    req = {}
+    http = "GET / HTTP/1.1\r\nHost: localhost:8080\r\nDummy: Valid\tValue\r\n\r\n"
+
+    parser.execute(req, http, 0)
+
+    assert_equal "Valid\tValue", req['HTTP_DUMMY']
+  end
 end

Vulnerable code: 	goto tr25;
#line 442 "ext/puma_http11/http11_parser.c"
	if ( (*p) == 13 )
		goto tr29;
	goto st19;
#line 488 "ext/puma_http11/http11_parser.c"
#line 509 "ext/puma_http11/http11_parser.c"
#line 530 "ext/puma_http11/http11_parser.c"
#line 555 "ext/puma_http11/http11_parser.c"
#line 575 "ext/puma_http11/http11_parser.c"
#line 598 "ext/puma_http11/http11_parser.c"
#line 618 "ext/puma_http11/http11_parser.c"
  field_value = any* >start_value %write_value;
	   39,   41,   44,   45,   61,   62,   78,   80,   81,   89,   97,  107,
	  115,  124,  132,  140,  149,  158,  167,  176,  185,  194,  203,  212,
	  221,  230,  239,  248,  257,  266,  275,  284,  293,  302,  303
	   48,   57,   65,   90,   94,  122,   13,   32,   13,   32,   60,   62,
	  127,    0,   31,   34,   35,   32,   60,   62,  127,    0,   31,   34,
	   35,   43,   58,   45,   46,   48,   57,   65,   90,   97,  122,   32,
	   34,   35,   60,   62,  127,    0,   31,   32,   34,   35,   60,   62,
	   63,  127,    0,   31,   32,   34,   35,   60,   62,  127,    0,   31,
	   32,   34,   35,   60,   62,  127,    0,   31,   32,   36,   95,   45,
	   46,   48,   57,   65,   90,   32,   36,   95,   45,   46,   48,   57,
	   65,   90,   32,   36,   95,   45,   46,   48,   57,   65,   90,   32,
	   65,   90,   32,    0
	    0,    1,    1,    4,    1,    4,    2,    1,    4,    4,    2,    6,
	    1,    1,    0,    6,    0,    6,    0,    0,    2,    2,    4,    1,
	   39,   41,   44,   46,   57,   59,   70,   73,   75,   82,   89,   96,
	  104,  113,  121,  129,  136,  143,  150,  157,  164,  171,  178,  185,
	  192,  199,  206,  213,  220,  227,  234,  241,  248,  255,  257
	   24,   23,   23,   23,   23,   23,   23,   23,   23,    1,   26,   27,
	   25,   29,   28,   30,    1,    1,    1,    1,    1,   31,   32,    1,
	    1,    1,    1,    1,   33,   34,   35,   34,   34,   34,   34,    1,
	    8,    1,    9,    1,    1,    1,    1,   35,   36,    1,   38,    1,
	    1,   39,    1,    1,   37,   40,    1,   42,    1,    1,    1,    1,
	   41,   43,    1,   45,    1,    1,    1,    1,   44,    2,   46,   46,
	   46,   46,   46,    1,    2,   47,   47,   47,   47,   47,    1,    2,
	   48,   48,   48,   48,   48,    1,    2,   49,   49,   49,   49,   49,
	    1,    2,   50,   50,   50,   50,   50,    1,    2,   51,   51,   51,
	   51,   51,    1,    2,   52,   52,   52,   52,   52,    1,    2,   53,
	   53,   53,   53,   53,    1,    2,   54,   54,   54,   54,   54,    1,
	    2,   55,   55,   55,   55,   55,    1,    2,   56,   56,   56,   56,
	   56,    1,    2,   57,   57,   57,   57,   57,    1,    2,   58,   58,
	   58,   58,   58,    1,    2,   59,   59,   59,   59,   59,    1,    2,
	   60,   60,   60,   60,   60,    1,    2,   61,   61,   61,   61,   61,
	    1,    2,   62,   62,   62,   62,   62,    1,    2,   63,   63,   63,
	   63,   63,    1,    2,    1,    1,    0
    PUMA_VERSION = VERSION = "5.5.0".freeze
Fixed code: ## 5.5.1 / 2021-10-12

* Security
  * Do not allow LF as a line ending in a header (CVE-2021-41136)

## 4.3.9 / 2021-10-12

* Security
  * Do not allow LF as a line ending in a header (CVE-2021-41136)

		case 9: goto tr25;
	if ( 33 <= (*p) && (*p) <= 126 )
		goto tr25;
	goto st0;
#line 445 "ext/puma_http11/http11_parser.c"
	switch( (*p) ) {
		case 9: goto st19;
		case 13: goto tr29;
	}
	if ( 32 <= (*p) && (*p) <= 126 )
		goto st19;
	goto st0;
#line 495 "ext/puma_http11/http11_parser.c"
#line 516 "ext/puma_http11/http11_parser.c"
#line 537 "ext/puma_http11/http11_parser.c"
#line 562 "ext/puma_http11/http11_parser.c"
#line 582 "ext/puma_http11/http11_parser.c"
#line 605 "ext/puma_http11/http11_parser.c"
#line 625 "ext/puma_http11/http11_parser.c"
  field_value = ( print | "\t" )* >start_value %write_value;
	   39,   41,   44,   45,   61,   62,   78,   83,   87,   95,  103,  113,
	  121,  130,  138,  146,  155,  164,  173,  182,  191,  200,  209,  218,
	  227,  236,  245,  254,  263,  272,  281,  290,  299,  308,  309
	   48,   57,   65,   90,   94,  122,    9,   13,   32,   33,  126,    9,
	   13,   32,  126,   32,   60,   62,  127,    0,   31,   34,   35,   32,
	   60,   62,  127,    0,   31,   34,   35,   43,   58,   45,   46,   48,
	   57,   65,   90,   97,  122,   32,   34,   35,   60,   62,  127,    0,
	   31,   32,   34,   35,   60,   62,   63,  127,    0,   31,   32,   34,
	   35,   60,   62,  127,    0,   31,   32,   34,   35,   60,   62,  127,
	    0,   31,   32,   36,   95,   45,   46,   48,   57,   65,   90,   32,
	   65,   90,   32,   36,   95,   45,   46,   48,   57,   65,   90,   32,
	   36,   95,   45,   46,   48,   57,   65,   90,   32,    0
	    0,    1,    1,    4,    1,    4,    3,    2,    4,    4,    2,    6,
	    1,    1,    0,    6,    0,    6,    1,    1,    2,    2,    4,    1,
	   39,   41,   44,   46,   57,   59,   70,   75,   79,   86,   93,  100,
	  108,  117,  125,  133,  140,  147,  154,  161,  168,  175,  182,  189,
	  196,  203,  210,  217,  224,  231,  238,  245,  252,  259,  261
	   24,   23,   23,   23,   23,   23,   23,   23,   23,    1,   25,   26,
	   27,   25,    1,   28,   29,   28,    1,   30,    1,    1,    1,    1,
	    1,   31,   32,    1,    1,    1,    1,    1,   33,   34,   35,   34,
	   34,   34,   34,    1,    8,    1,    9,    1,    1,    1,    1,   35,
	   36,    1,   38,    1,    1,   39,    1,    1,   37,   40,    1,   42,
	    1,    1,    1,    1,   41,   43,    1,   45,    1,    1,    1,    1,
	   44,    2,   46,   46,   46,   46,   46,    1,    2,   47,   47,   47,
	   47,   47,    1,    2,   48,   48,   48,   48,   48,    1,    2,   49,
	   49,   49,   49,   49,    1,    2,   50,   50,   50,   50,   50,    1,
	    2,   51,   51,   51,   51,   51,    1,    2,   52,   52,   52,   52,
	   52,    1,    2,   53,   53,   53,   53,   53,    1,    2,   54,   54,
	   54,   54,   54,    1,    2,   55,   55,   55,   55,   55,    1,    2,
	   56,   56,   56,   56,   56,    1,    2,   57,   57,   57,   57,   57,
	    1,    2,   58,   58,   58,   58,   58,    1,    2,   59,   59,   59,
	   59,   59,    1,    2,   60,   60,   60,   60,   60,    1,    2,   61,
	   61,   61,   61,   61,    1,    2,   62,   62,   62,   62,   62,    1,
	    2,   63,   63,   63,   63,   63,    1,    2,    1,    1,    0
    PUMA_VERSION = VERSION = "5.5.1".freeze

  def test_newline_smuggler
    parser = Puma::HttpParser.new
    req = {}
    http = "GET / HTTP/1.1\r\nHost: localhost:8080\r\nDummy: x\nDummy2: y\r\n\r\n"

    parser.execute(req, http, 0) rescue nil # We test the raise elsewhere.

    assert parser.error?, "Parser SHOULD have error"
  end

  def test_newline_smuggler_two
    parser = Puma::HttpParser.new
    req = {}
    http = "GET / HTTP/1.1\r\nHost: localhost:8080\r\nDummy: x\r\nDummy: y\nDummy2: z\r\n\r\n"

    parser.execute(req, http, 0) rescue nil

    assert parser.error?, "Parser SHOULD have error"
  end

  def test_htab_in_header_val
    parser = Puma::HttpParser.new
    req = {}
    http = "GET / HTTP/1.1\r\nHost: localhost:8080\r\nDummy: Valid\tValue\r\n\r\n"

    parser.execute(req, http, 0)

    assert_equal "Valid\tValue", req['HTTP_DUMMY']
  end