Merge pull request #55852 from nextcloud/backport/55851/stable31

[stable31] fix(dav): Restrict properties allowed object classes
pull/55877/head
Joas Schilling 2025-10-20 13:21:14 +07:00 committed by GitHub
commit bf8db3175e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
1 changed files with 38 additions and 3 deletions

@ -480,6 +480,18 @@ class CustomPropertiesBackend implements BackendInterface {
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),
);
}
}
}
/**
* @throws ParseException If parsing a \Sabre\DAV\Xml\Property\Complex value fails
* @throws DavException If the property value is invalid
@ -514,6 +526,23 @@ class CustomPropertiesBackend implements BackendInterface {
$valueType = self::PROPERTY_TYPE_HREF;
$value = $value->getHref();
} 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
@ -525,20 +554,26 @@ class CustomPropertiesBackend implements BackendInterface {
/**
* @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_HREF:
return new Href($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 encodeDefaultCalendarUrl(Href $value): Href {