From 7d8e6c6cf59ea0b5004960b8402a39f89a9e512a Mon Sep 17 00:00:00 2001 From: Matthew Weier O'Phinney Date: Tue, 19 May 2015 16:24:00 -0500 Subject: [PATCH 1/2] Cast int and float to string when creating headers With the ZF2015-04 patch, we were no longer allowing non-string, non-stringable objects as header values. This broke a number of other classes, however, which required integer and/or float values (e.g., to set a Content-Length header). This patch casts those types to strings before attempting to set them as header values. --- library/Zend/Http/Client.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/library/Zend/Http/Client.php b/library/Zend/Http/Client.php index 279556a9ca..b704249cc2 100644 --- a/library/Zend/Http/Client.php +++ b/library/Zend/Http/Client.php @@ -1592,6 +1592,11 @@ protected function _validateHeaderValue($value, $recurse = true) return; } + // Cast integers and floats to strings for purposes of header representation. + if (is_int($value) || is_float($value)) { + $value = (string) $value; + } + if (! is_string($value) && (! is_object($value) || ! method_exists($value, '__toString'))) { require_once 'Zend/Http/Exception.php'; throw new Zend_Http_Exception('Invalid header value detected'); From b07f4eae8f9c15c4b79de9c9f909c06be9e5eb12 Mon Sep 17 00:00:00 2001 From: Matthew Weier O'Phinney Date: Tue, 19 May 2015 17:39:13 -0500 Subject: [PATCH 2/2] Fixes for unit tests Man unit tests sufferred from the same issues as the Zend_Http_* tests, in that the canned responses had incorrect line endings in them, making them fail the new logic implemented to protect against message splitting. These have now all been updated. --- .../Cloud/Infrastructure/Adapter/Ec2Test.php | 8 ++++- .../Infrastructure/Adapter/RackspaceTest.php | 12 +++++-- tests/Zend/Gdata/App/EntryTest.php | 23 ++++++++---- tests/Zend/Gdata/AppTest.php | 35 ++++++++++++------- tests/Zend/Gdata/AuthSubTest.php | 7 ++-- tests/Zend/Http/Client/StaticTest.php | 17 +++++---- tests/Zend/Http/CookieJarTest.php | 25 +++++++++---- .../Audioscrobbler/AudioscrobblerTest.php | 7 +++- tests/Zend/Service/Flickr/OfflineTest.php | 7 +++- 9 files changed, 97 insertions(+), 44 deletions(-) diff --git a/tests/Zend/Cloud/Infrastructure/Adapter/Ec2Test.php b/tests/Zend/Cloud/Infrastructure/Adapter/Ec2Test.php index 4eb5bfe1fc..def80e598f 100644 --- a/tests/Zend/Cloud/Infrastructure/Adapter/Ec2Test.php +++ b/tests/Zend/Cloud/Infrastructure/Adapter/Ec2Test.php @@ -91,7 +91,13 @@ public function setUp() */ protected function loadResponse($name) { - return file_get_contents($name); + $response = file_get_contents($name); + + // Line endings are sometimes an issue inside the canned responses; the + // following is a negative lookbehind assertion, and replaces any \n + // not preceded by \r with the sequence \r\n, ensuring that the message + // is well-formed. + return preg_replace("#(?httpClientAdapterTest->setResponse($content); + $content = dirname(__FILE__) . '/_files/'.$shortClassName . '_testAuthenticate.response'; + $this->httpClientAdapterTest->setResponse($this->loadResponse($content)); $this->assertTrue($this->infrastructure->getAdapter()->authenticate(),'Authentication failed'); $this->httpClientAdapterTest->setResponse($this->loadResponse($filename)); @@ -91,7 +91,13 @@ public function setUp() */ protected function loadResponse($name) { - return file_get_contents($name); + $response = file_get_contents($name); + + // Line endings are sometimes an issue inside the canned responses; the + // following is a negative lookbehind assertion, and replaces any \n + // not preceded by \r with the sequence \r\n, ensuring that the message + // is well-formed. + return preg_replace("#(?enryText = file_get_contents( - 'Zend/Gdata/App/_files/EntrySample1.xml', - true); - $this->httpEntrySample = file_get_contents( - 'Zend/Gdata/App/_files/EntrySampleHttp1.txt', - true); + $this->enryText = $this->loadResponse( + dirname(__FILE__) . '/../App/_files/EntrySample1.xml' + ); + $this->httpEntrySample = $this->loadResponse( + dirname(__FILE__) . '/../App/_files/EntrySampleHttp1.txt' + ); $this->enry = new Zend_Gdata_App_Entry(); $this->adapter = new Test_Zend_Gdata_MockHttpClient(); @@ -53,6 +53,17 @@ public function setUp() $this->service = new Zend_Gdata_App($this->client); } + public function loadResponse($filename) + { + $response = file_get_contents($filename); + + // Line endings are sometimes an issue inside the canned responses; the + // following is a negative lookbehind assertion, and replaces any \n + // not preceded by \r with the sequence \r\n, ensuring that the message + // is well-formed. + return preg_replace("#(?assertTrue(is_array($this->enry->extensionElements)); diff --git a/tests/Zend/Gdata/AppTest.php b/tests/Zend/Gdata/AppTest.php index cb1ebf19b1..0b02b5d87d 100644 --- a/tests/Zend/Gdata/AppTest.php +++ b/tests/Zend/Gdata/AppTest.php @@ -41,18 +41,18 @@ public function setUp() $this->expectedEtag = 'W/"CkcHQH8_fCp7ImA9WxRTGEw."'; $this->expectedMajorProtocolVersion = 1; $this->expectedMinorProtocolVersion = 2; - $this->httpEntrySample = file_get_contents( - 'Zend/Gdata/_files/AppSample1.txt', - true); - $this->httpEntrySampleWithoutVersion = file_get_contents( - 'Zend/Gdata/_files/AppSample2.txt', - true); - $this->httpFeedSample = file_get_contents( - 'Zend/Gdata/_files/AppSample3.txt', - true); - $this->httpFeedSampleWithoutVersion = file_get_contents( - 'Zend/Gdata/_files/AppSample4.txt', - true); + $this->httpEntrySample = $this->loadResponse( + dirname(__FILE__) . '/_files/AppSample1.txt' + ); + $this->httpEntrySampleWithoutVersion = $this->loadResponse( + dirname(__FILE__) . '/_files/AppSample2.txt' + ); + $this->httpFeedSample = $this->loadResponse( + dirname(__FILE__) . '/_files/AppSample3.txt' + ); + $this->httpFeedSampleWithoutVersion = $this->loadResponse( + dirname(__FILE__) . '/_files/AppSample4.txt' + ); $this->adapter = new Test_Zend_Gdata_MockHttpClient(); $this->client = new Zend_Gdata_HttpClient(); @@ -60,6 +60,17 @@ public function setUp() $this->service = new Zend_Gdata_App($this->client); } + public function loadResponse($filename) + { + $response = file_get_contents($filename); + + // Line endings are sometimes an issue inside the canned responses; the + // following is a negative lookbehind assertion, and replaces any \n + // not preceded by \r with the sequence \r\n, ensuring that the message + // is well-formed. + return preg_replace("#(?fileName, diff --git a/tests/Zend/Gdata/AuthSubTest.php b/tests/Zend/Gdata/AuthSubTest.php index 0e27932d71..b44a19ef5e 100755 --- a/tests/Zend/Gdata/AuthSubTest.php +++ b/tests/Zend/Gdata/AuthSubTest.php @@ -221,11 +221,8 @@ public function testAuthSubRevokeTokenCatchesHttpClientException() public function testGetAuthSubTokenInfoReceivesSuccessfulResult() { $adapter = new Zend_Http_Client_Adapter_Test(); - $adapter->setResponse("HTTP/1.1 200 OK - -Target=http://example.com -Scope=http://example.com -Secure=false"); + $response = "HTTP/1.1 200 OK\r\n\r\nTarget=http://example.com\nScope=http://example.com\nSecure=false"; + $adapter->setResponse($response); $client = new Zend_Gdata_HttpClient(); $client->setUri('http://example.com/AuthSub'); diff --git a/tests/Zend/Http/Client/StaticTest.php b/tests/Zend/Http/Client/StaticTest.php index e0629f4c82..f4115e3b4e 100644 --- a/tests/Zend/Http/Client/StaticTest.php +++ b/tests/Zend/Http/Client/StaticTest.php @@ -689,15 +689,14 @@ public function testRedirectWithTrailingSpaceInLocationHeaderZF11283() $adapter = $this->_client->getAdapter(); /* @var $adapter Zend_Http_Client_Adapter_Test */ - $adapter->setResponse(<<setResponse($response); $res = $this->_client->request('GET'); diff --git a/tests/Zend/Http/CookieJarTest.php b/tests/Zend/Http/CookieJarTest.php index ffed4351fd..732ffc6528 100644 --- a/tests/Zend/Http/CookieJarTest.php +++ b/tests/Zend/Http/CookieJarTest.php @@ -35,6 +35,16 @@ */ class Zend_Http_CookieJarTest extends PHPUnit_Framework_TestCase { + public function loadResponse($filename) + { + $message = file_get_contents($filename); + // Line endings are sometimes an issue inside the canned responses; the + // following is a negative lookbehind assertion, and replaces any \n + // not preceded by \r with the sequence \r\n, ensuring that the message + // is well-formed. + return preg_replace("#(?loadResponse( + dirname(realpath(__FILE__)) . '/_files/response_with_cookies' + ); $response = Zend_Http_Response::fromString($res_str); $jar->addCookiesFromResponse($response, 'http://www.example.com'); @@ -442,8 +453,9 @@ public function testExceptGetMatchingCookiesInvalidUri() */ public function testFromResponse() { - $res_str = file_get_contents(dirname(realpath(__FILE__)) . - DIRECTORY_SEPARATOR . '_files' . DIRECTORY_SEPARATOR . 'response_with_single_cookie'); + $res_str = $this->loadResponse( + dirname(realpath(__FILE__)) . '/_files/response_with_single_cookie' + ); $response = Zend_Http_Response::fromString($res_str); $jar = Zend_Http_CookieJar::fromResponse($response, 'http://www.example.com'); @@ -457,8 +469,9 @@ public function testFromResponse() */ public function testFromResponseMultiHeader() { - $res_str = file_get_contents(dirname(realpath(__FILE__)) . - DIRECTORY_SEPARATOR . '_files' . DIRECTORY_SEPARATOR . 'response_with_cookies'); + $res_str = $this->loadResponse( + dirname(realpath(__FILE__)) . '/_files/response_with_cookies' + ); $response = Zend_Http_Response::fromString($res_str); $jar = Zend_Http_CookieJar::fromResponse($response, 'http://www.example.com'); diff --git a/tests/Zend/Service/Audioscrobbler/AudioscrobblerTest.php b/tests/Zend/Service/Audioscrobbler/AudioscrobblerTest.php index 09a146f212..982ae9bdd4 100644 --- a/tests/Zend/Service/Audioscrobbler/AudioscrobblerTest.php +++ b/tests/Zend/Service/Audioscrobbler/AudioscrobblerTest.php @@ -102,6 +102,11 @@ public function testCallInterceptMethodsRequireExactlyOneParameterAndThrowExcept public static function readTestResponse($file) { - return file_get_contents(dirname(__FILE__) . DIRECTORY_SEPARATOR . '_files' . DIRECTORY_SEPARATOR . $file); + $message = file_get_contents(sprintf('%s/_files/%s', dirname(__FILE__), $file)); + // Line endings are sometimes an issue inside the canned responses; the + // following is a negative lookbehind assertion, and replaces any \n + // not preceded by \r with the sequence \r\n, ensuring that the message + // is well-formed. + return preg_replace("#(?_filesPath/$name.response"); + $message = file_get_contents(sprintf('%s/%s.response', $this->_filesPath, $name)); + // Line endings are sometimes an issue inside the canned responses; the + // following is a negative lookbehind assertion, and replaces any \n + // not preceded by \r with the sequence \r\n, ensuring that the message + // is well-formed. + return preg_replace("#(?