diff --git a/apps/dav/lib/DAV/CustomPropertiesBackend.php b/apps/dav/lib/DAV/CustomPropertiesBackend.php index 1777aab657c2c..470d332d27b94 100644 --- a/apps/dav/lib/DAV/CustomPropertiesBackend.php +++ b/apps/dav/lib/DAV/CustomPropertiesBackend.php @@ -31,6 +31,7 @@ use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\IDBConnection; use OCP\IUser; +use Sabre\DAV\Exception as DavException; use Sabre\DAV\PropertyStorage\Backend\BackendInterface; use Sabre\DAV\PropFind; use Sabre\DAV\PropPatch; @@ -393,7 +394,7 @@ private function updateProperties(string $path, array $properties): bool { ->executeStatement(); } } else { - [$value, $valueType] = $this->encodeValueForDatabase($propertyValue); + [$value, $valueType] = $this->encodeValueForDatabase($propertyName, $propertyValue); $dbParameters['propertyValue'] = $value; $dbParameters['valueType'] = $valueType; @@ -435,17 +436,46 @@ private function formatPath(string $path): string { return $path; } + private static function checkIsArrayOfScalar(string $name, array $array): void { + foreach ($array as $item) { + if (is_array($item)) { + self::checkIsArrayOfScalar($name, $item); + } elseif ($item !== null && !is_scalar($item)) { + throw new DavException( + "Property \"$name\" has an invalid value of array containing " . gettype($item), + ); + } + } + } + /** * @param mixed $value * @return array */ - private function encodeValueForDatabase($value): array { + private function encodeValueForDatabase(string $name, $value): array { if (is_scalar($value)) { $valueType = self::PROPERTY_TYPE_STRING; } elseif ($value instanceof Complex) { $valueType = self::PROPERTY_TYPE_XML; $value = $value->getXml(); } else { + if (is_array($value)) { + // For array only allow scalar values + self::checkIsArrayOfScalar($name, $value); + } elseif (!is_object($value)) { + throw new DavException( + "Property \"$name\" has an invalid value of type " . gettype($value), + ); + } else { + if (!str_starts_with($value::class, 'Sabre\\DAV\\Xml\\Property\\') + && !str_starts_with($value::class, 'Sabre\\CalDAV\\Xml\\Property\\') + && !str_starts_with($value::class, 'Sabre\\CardDAV\\Xml\\Property\\') + && !str_starts_with($value::class, 'OCA\\DAV\\')) { + throw new DavException( + "Property \"$name\" has an invalid value of class " . $value::class, + ); + } + } $valueType = self::PROPERTY_TYPE_OBJECT; // serialize produces null character // these can not be properly stored in some databases and need to be replaced @@ -457,18 +487,24 @@ private function encodeValueForDatabase($value): array { /** * @return mixed|Complex|string */ - private function decodeValueFromDatabase(string $value, int $valueType) { + private function decodeValueFromDatabase(string $value, int $valueType): mixed { switch ($valueType) { case self::PROPERTY_TYPE_XML: return new Complex($value); case self::PROPERTY_TYPE_OBJECT: + if (preg_match('/^a:/', $value)) { + // Array, unserialize only scalar values + return unserialize(str_replace('\x00', chr(0), $value), ['allowed_classes' => false]); + } + if (!preg_match('/^O\:\d+\:\"(OCA\\\\DAV\\\\|Sabre\\\\(Cal|Card)?DAV\\\\Xml\\\\Property\\\\)/', $value)) { + throw new \LogicException('Found an object class serialized in DB that is not allowed'); + } // some databases can not handel null characters, these are custom encoded during serialization // this custom encoding needs to be first reversed before unserializing return unserialize(str_replace('\x00', chr(0), $value)); - case self::PROPERTY_TYPE_STRING: default: return $value; - } + }; } private function createDeleteQuery(): IQueryBuilder {