fs: improve sanitization

Signed-off-by: Varun Patil <radialapps@gmail.com>
pull/888/head
Varun Patil 2023-10-19 18:47:58 -07:00
parent 73cc86c4a1
commit f69765a42a
4 changed files with 35 additions and 17 deletions

View File

@ -72,6 +72,7 @@ class ArchiveController extends GenericApiController
$fileStorageId = $file->getStorage()->getId();
$parent = $file->getParent();
$isArchived = false;
$depth = 0;
while (true) {
/** @psalm-suppress DocblockTypeContradiction */
if (null === $parent) {
@ -98,12 +99,17 @@ class ArchiveController extends GenericApiController
}
// Hit an archive folder root
if ($parent->getName() === \OCA\Memories\Util::$ARCHIVE_FOLDER) {
if (Util::ARCHIVE_FOLDER === $parent->getName()) {
$isArchived = true;
break;
}
// Too deep
if (++$depth > 32) {
throw new \Exception('[Archive] Max recursion depth exceeded');
}
$parent = $parent->getParent();
}
@ -133,8 +139,10 @@ class ArchiveController extends GenericApiController
$parent = $parent->getParent();
} else {
// file not in archive, put it in there
$af = \OCA\Memories\Util::$ARCHIVE_FOLDER;
$destinationPath = Util::sanitizePath($af.$relativeFilePath);
$destinationPath = Util::sanitizePath(Util::ARCHIVE_FOLDER.$relativeFilePath);
if (null === $destinationPath) {
throw Exceptions::BadRequest('Invalid archive destination path');
}
}
// Remove the filename

View File

@ -189,10 +189,11 @@ class PublicController extends AuthPublicShareController
$foldersPath = Util::sanitizePath('/'.$foldersPath.'/');
// Check if relPath starts with foldersPath
if (!str_starts_with($relPath, $foldersPath)) {
if (empty($foldersPath) || !str_starts_with($relPath, $foldersPath)) {
return;
}
/** @var string $foldersPath */
// Remove foldersPath from start of relPath
$relPath = substr($relPath, \strlen($foldersPath));

View File

@ -114,14 +114,16 @@ class FsManager
try {
foreach ($paths as $path) {
$node = $userFolder->get(Util::sanitizePath($path));
if ($sanitized = Util::sanitizePath($path)) {
$node = $userFolder->get($sanitized);
$root->addFolder($node);
$etag .= $node->getEtag();
} else {
throw new \Exception("invalid path {$path}");
}
} catch (\OCP\Files\NotFoundException $e) {
$msg = $e->getMessage();
throw new \Exception("Folder not found: {$msg}");
}
} catch (\Exception $e) {
throw new \Exception("Folder not found: {$e->getMessage()}");
}
// Add shares or external stores inside the current folders

View File

@ -18,7 +18,7 @@ class Util
{
use UtilController;
public static string $ARCHIVE_FOLDER = '.archive';
public const ARCHIVE_FOLDER = '.archive';
/**
* Get host CPU architecture (amd64 or aarch64).
@ -336,20 +336,27 @@ class Util
?: self::getSystemConfig('memories.timeline.default_path');
return array_map(
static fn ($p) => self::sanitizePath(trim($p)),
static fn ($path) => self::sanitizePath(trim($path))
?? throw new \InvalidArgumentException("Invalid timeline path: {$path}"),
explode(';', $paths),
);
}
/**
* Sanitize a path to keep only ASCII characters and special characters.
* Blank will be returned on error.
* Null will be returned on error.
*/
public static function sanitizePath(string $path): string
public static function sanitizePath(string $path): ?string
{
$path = str_replace("\0", '', $path); // remove null characters
// remove double slashes and such
$normalized = \OC\Files\Filesystem::normalizePath($path, false);
return mb_ereg_replace('\/\/+', '/', $path) ?: ''; // remove extra slashes
// look for invalid characters and pattern
if (!\OC\Files\Filesystem::isValidPath($normalized)) {
return null;
}
return $normalized;
}
/**