Explorar el Código

fix: reject malformed JSON API bodies instead of crashing

Address review feedback on #1885:

- reject valid JSON scalar bodies (number, bool, string) that decode
  without error but are not a usable parameter set, so they no longer
  trigger a type error further down (Request::getParams)
- drop the numeric coercion of `v` in Request::getData() and let the
  FormatV2 validator reject non-numeric versions, as suggested
- add RequestTest::testPostScalarJson coverage for scalar bodies
- align FormatV2Test assignments per StyleCI
- add CHANGELOG entry referencing #1883
marouanehassine hace 1 semana
padre
commit
abd1418bc8
Se han modificado 4 ficheros con 29 adiciones y 10 borrados
  1. 1 0
      CHANGELOG.md
  2. 5 7
      lib/Request.php
  3. 3 3
      tst/FormatV2Test.php
  4. 20 0
      tst/RequestTest.php

+ 1 - 0
CHANGELOG.md

@@ -3,6 +3,7 @@
 ## 2.0.6 (not yet released)
 * CHANGED: Upgrading libraries to: DOMpurify 3.4.12
 * FIXED: Gracefully handle YOURLS replies with a 200 status code but no shorturl, instead of raising a TypeError
+* FIXED: Return "Invalid data." instead of HTTP 500 on malformed v2 JSON payloads (#1883)
 
 ## 2.0.5 (2026-07-11)
 * CHANGED: Show OS-specific copy hotkey hint (Cmd+c on Mac, Ctrl+c on others) (#1506)

+ 5 - 7
lib/Request.php

@@ -113,6 +113,11 @@ class Request
                 try {
                     $data          = file_get_contents(self::$_inputStream);
                     $this->_params = Json::decode($data);
+                    // a valid JSON scalar (number, bool or string) decodes
+                    // without error, but is not a usable set of parameters
+                    if (!is_array($this->_params)) {
+                        $this->_params = array();
+                    }
                 } catch (JsonException $e) {
                     // ignore error, $this->_params will remain empty
                 }
@@ -189,13 +194,6 @@ class Request
         foreach ($required_keys as $key) {
             $data[$key] = $this->getParam($key, $key === 'v' ? 1 : '');
         }
-        // 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;
     }
 

+ 3 - 3
tst/FormatV2Test.php

@@ -93,15 +93,15 @@ class FormatV2Test extends TestCase
         $paste['meta']  = 'not-an-array';
         $this->assertFalse(FormatV2::isValid($paste), 'non-array meta');
 
-        $paste          = Helper::getPastePost();
+        $paste             = Helper::getPastePost();
         $paste['adata'][0] = 'not-an-array';
         $this->assertFalse(FormatV2::isValid($paste), 'non-array cipher parameters');
 
-        $paste          = Helper::getPastePost();
+        $paste             = Helper::getPastePost();
         $paste['adata'][0] = array();
         $this->assertFalse(FormatV2::isValid($paste), 'empty cipher parameters');
 
-        $paste             = Helper::getPastePost();
+        $paste                = Helper::getPastePost();
         $paste['adata'][0][0] = array();
         $this->assertFalse(FormatV2::isValid($paste), 'non-string iv');
 

+ 20 - 0
tst/RequestTest.php

@@ -158,6 +158,26 @@ class RequestTest extends TestCase
         $this->assertEquals('create', $request->getOperation());
     }
 
+    public function testPostScalarJson()
+    {
+        // a valid JSON scalar body (number, boolean or quoted string) must not
+        // be treated as a set of parameters, so that it is rejected cleanly
+        // instead of triggering a type error further down
+        foreach (array('1', '0.0', 'true', 'false', '"example"') as $scalar) {
+            $this->reset();
+            $_SERVER['REQUEST_METHOD']        = 'POST';
+            $_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
+            $file                             = Helper::createTempFile();
+            file_put_contents($file, $scalar);
+            Request::setInputStream($file);
+            $request = new Request;
+            unlink($file);
+            $this->assertTrue($request->isJsonApiCall(), 'is JSON API call');
+            $this->assertEquals('create', $request->getOperation());
+            $this->assertEquals('', $request->getParam('ct'), "scalar body {$scalar} yields no parameters");
+        }
+    }
+
     public function testReadWithNegotiation()
     {
         $this->reset();