diff --git a/lib/FormatV2.php b/lib/FormatV2.php index 7f1912fa..b7e749f9 100644 --- a/lib/FormatV2.php +++ b/lib/FormatV2.php @@ -56,7 +56,21 @@ class FormatV2 return false; } - $cipherParams = $isComment ? $message['adata'] : $message['adata'][0]; + $cipherParams = $isComment ? $message['adata'] : ($message['adata'][0] ?? null); + + // Make sure the cipher parameters are a properly sized array. + if (!is_array($cipherParams) || count($cipherParams) < 8) { + return false; + } + + // Make sure the ciphertext and the cipher parameters used in the + // string operations below are actually strings, so that malformed + // input yields "Invalid data." instead of a fatal type error. + if (!is_string($message['ct']) || + !is_string($cipherParams[0] ?? null) || + !is_string($cipherParams[1] ?? null)) { + return false; + } // Make sure some fields are base64 data: // - initialization vector @@ -119,6 +133,7 @@ class FormatV2 // require only the key 'expire' in the metadata of pastes if (!$isComment && ( + !is_array($message['meta']) || count($message['meta']) === 0 || !array_key_exists('expire', $message['meta']) || count($message['meta']) > 1 diff --git a/lib/Request.php b/lib/Request.php index 31233472..c1902b72 100644 --- a/lib/Request.php +++ b/lib/Request.php @@ -189,8 +189,13 @@ class Request foreach ($required_keys as $key) { $data[$key] = $this->getParam($key, $key === 'v' ? 1 : ''); } - // forcing a cast to int or float - $data['v'] = $data['v'] + 0; + // forcing a cast to int or float, but only when the value is + // numeric; a non-numeric version is left untouched so the format + // validator rejects it with "Invalid data." instead of a fatal + // type error + if (is_numeric($data['v'])) { + $data['v'] = $data['v'] + 0; + } return $data; } diff --git a/tst/FormatV2Test.php b/tst/FormatV2Test.php index 2e13d8f5..b5dfca1b 100644 --- a/tst/FormatV2Test.php +++ b/tst/FormatV2Test.php @@ -72,4 +72,41 @@ class FormatV2Test extends TestCase $paste = Helper::getPaste(); $this->assertFalse(FormatV2::isValid($paste), 'invalid meta key'); } + + public function testFormatV2ValidatorRejectsMalformedTypes() + { + // malformed input must yield "false" (Invalid data) rather than a + // fatal type error under strict_types + $paste = Helper::getPastePost(); + $paste['v'] = 'abc'; + $this->assertFalse(FormatV2::isValid($paste), 'non-numeric version'); + + $paste = Helper::getPastePost(); + $paste['v'] = array(); + $this->assertFalse(FormatV2::isValid($paste), 'array version'); + + $paste = Helper::getPastePost(); + $paste['ct'] = array('not', 'a', 'string'); + $this->assertFalse(FormatV2::isValid($paste), 'non-string ciphertext'); + + $paste = Helper::getPastePost(); + $paste['meta'] = 'not-an-array'; + $this->assertFalse(FormatV2::isValid($paste), 'non-array meta'); + + $paste = Helper::getPastePost(); + $paste['adata'][0] = 'not-an-array'; + $this->assertFalse(FormatV2::isValid($paste), 'non-array cipher parameters'); + + $paste = Helper::getPastePost(); + $paste['adata'][0] = array(); + $this->assertFalse(FormatV2::isValid($paste), 'empty cipher parameters'); + + $paste = Helper::getPastePost(); + $paste['adata'][0][0] = array(); + $this->assertFalse(FormatV2::isValid($paste), 'non-string iv'); + + $comment = Helper::getCommentPost(); + $comment['ct'] = 42; + $this->assertFalse(FormatV2::isValid($comment, true), 'non-string comment ciphertext'); + } }