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

View File

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

View File

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

View File

@ -18,7 +18,7 @@ class Util
{ {
use UtilController; use UtilController;
public static string $ARCHIVE_FOLDER = '.archive'; public const ARCHIVE_FOLDER = '.archive';
/** /**
* Get host CPU architecture (amd64 or aarch64). * Get host CPU architecture (amd64 or aarch64).
@ -336,20 +336,27 @@ class Util
?: self::getSystemConfig('memories.timeline.default_path'); ?: self::getSystemConfig('memories.timeline.default_path');
return array_map( 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), explode(';', $paths),
); );
} }
/** /**
* Sanitize a path to keep only ASCII characters and special characters. * 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;
} }
/** /**