fix(ocs): Return a proper error on JSON decoding failures

Signed-off-by: Côme Chilliet <come.chilliet@nextcloud.com>
pull/53740/head
Côme Chilliet 2025-07-01 10:42:29 +07:00
parent 87f98c88ab
commit e8bc35ec0a
No known key found for this signature in database
GPG Key ID: A3E2F658B28C760A
4 changed files with 32 additions and 7 deletions

@ -141,8 +141,8 @@ class OC {
// Resolve /nextcloud to /nextcloud/ to ensure to always have a trailing
// slash which is required by URL generation.
if (isset($_SERVER['REQUEST_URI']) && $_SERVER['REQUEST_URI'] === \OC::$WEBROOT &&
substr($_SERVER['REQUEST_URI'], -1) !== '/') {
if (isset($_SERVER['REQUEST_URI']) && $_SERVER['REQUEST_URI'] === \OC::$WEBROOT
&& substr($_SERVER['REQUEST_URI'], -1) !== '/') {
header('Location: ' . \OC::$WEBROOT . '/');
exit();
}
@ -285,8 +285,8 @@ class OC {
$tooBig = ($totalUsers > 50);
}
}
$ignoreTooBigWarning = isset($_GET['IKnowThatThisIsABigInstanceAndTheUpdateRequestCouldRunIntoATimeoutAndHowToRestoreABackup']) &&
$_GET['IKnowThatThisIsABigInstanceAndTheUpdateRequestCouldRunIntoATimeoutAndHowToRestoreABackup'] === 'IAmSuperSureToDoThis';
$ignoreTooBigWarning = isset($_GET['IKnowThatThisIsABigInstanceAndTheUpdateRequestCouldRunIntoATimeoutAndHowToRestoreABackup'])
&& $_GET['IKnowThatThisIsABigInstanceAndTheUpdateRequestCouldRunIntoATimeoutAndHowToRestoreABackup'] === 'IAmSuperSureToDoThis';
if ($disableWebUpdater || ($tooBig && !$ignoreTooBigWarning)) {
// send http status 503
@ -987,6 +987,7 @@ class OC {
}
$request = Server::get(IRequest::class);
$request->throwDecodingExceptionIfAny();
$requestPath = $request->getRawPathInfo();
if ($requestPath === '/heartbeat') {
return;

@ -64,6 +64,7 @@ class Request implements \ArrayAccess, \Countable, IRequest {
protected ?CsrfTokenManager $csrfTokenManager;
protected bool $contentDecoded = false;
private ?\JsonException $decodingException = null;
/**
* @param array $vars An associative array with the following optional values:
@ -389,7 +390,11 @@ class Request implements \ArrayAccess, \Countable, IRequest {
// 'application/json' and other JSON-related content types must be decoded manually.
if (preg_match(self::JSON_CONTENT_TYPE_REGEX, $this->getHeader('Content-Type')) === 1) {
$params = json_decode(file_get_contents($this->inputStream), true);
try {
$params = json_decode(file_get_contents($this->inputStream), true, flags:JSON_THROW_ON_ERROR);
} catch (\JsonException $e) {
$this->decodingException = $e;
}
if (\is_array($params) && \count($params) > 0) {
$this->items['params'] = $params;
if ($this->method === 'POST') {
@ -413,6 +418,12 @@ class Request implements \ArrayAccess, \Countable, IRequest {
$this->contentDecoded = true;
}
public function throwDecodingExceptionIfAny(): void {
if ($this->decodingException !== null) {
throw $this->decodingException;
}
}
/**
* Checks if the CSRF check was correct

@ -305,4 +305,14 @@ interface IRequest {
* @since 8.1.0
*/
public function getServerHost(): string;
/**
* If decoding the request content failed, throw an exception.
* Currently only \JsonException for json decoding errors,
* but in the future may throw other exceptions for other decoding issues.
*
* @throws \Exception
* @since 32.0.0
*/
public function throwDecodingExceptionIfAny(): void;
}

@ -50,11 +50,14 @@ try {
// side effects in existing apps
OC_App::loadApps();
$request = Server::get(IRequest::class);
$request->throwDecodingExceptionIfAny();
if (!Server::get(IUserSession::class)->isLoggedIn()) {
OC::handleLogin(Server::get(IRequest::class));
OC::handleLogin($request);
}
Server::get(Router::class)->match('/ocsapp' . Server::get(IRequest::class)->getRawPathInfo());
Server::get(Router::class)->match('/ocsapp' . $request->getRawPathInfo());
} catch (MaxDelayReached $ex) {
ApiHelper::respond(Http::STATUS_TOO_MANY_REQUESTS, $ex->getMessage());
} catch (ResourceNotFoundException $e) {