From c06d8bb0071839480f9e458e58630ca0c205b9cb Mon Sep 17 00:00:00 2001 From: Vincent Petry Date: Thu, 14 Nov 2013 13:15:03 +0100 Subject: [PATCH 1/3] Fixed normalizePath() to strip out single dot dirs Now removing "/./" and trailing "/." from the paths when normalizing. --- lib/private/files/filesystem.php | 20 ++++++++++-- tests/lib/files/filesystem.php | 55 +++++++++++++++++++++++++++++++- 2 files changed, 71 insertions(+), 4 deletions(-) diff --git a/lib/private/files/filesystem.php b/lib/private/files/filesystem.php index 899666f3e1a..8500b3c581b 100644 --- a/lib/private/files/filesystem.php +++ b/lib/private/files/filesystem.php @@ -689,18 +689,32 @@ class Filesystem { } //no windows style slashes $path = str_replace('\\', '/', $path); + //add leading slash if ($path[0] !== '/') { $path = '/' . $path; } - //remove duplicate slashes - while (strpos($path, '//') !== false) { - $path = str_replace('//', '/', $path); + + // remove '/./' + // ugly, but str_replace() can't replace them all in one go + // as the replacement itself is part of the search string + // which will only be found during the next iteration + while (strpos($path, '/./') !== false) { + $path = str_replace('/./', '/', $path); } + // remove sequences of slashes + $path = preg_replace('#/{2,}#', '/', $path); + //remove trailing slash if ($stripTrailingSlash and strlen($path) > 1 and substr($path, -1, 1) === '/') { $path = substr($path, 0, -1); } + + // remove trailing '/.' + if (substr($path, -2) == '/.') { + $path = substr($path, 0, -2); + } + //normalize unicode if possible $path = \OC_Util::normalizeUnicode($path); diff --git a/tests/lib/files/filesystem.php b/tests/lib/files/filesystem.php index 990e95ca595..e703a961299 100644 --- a/tests/lib/files/filesystem.php +++ b/tests/lib/files/filesystem.php @@ -69,17 +69,70 @@ class Filesystem extends \PHPUnit_Framework_TestCase { } public function testNormalize() { + $this->assertEquals('/', \OC\Files\Filesystem::normalizePath('')); + $this->assertEquals('/', \OC\Files\Filesystem::normalizePath('/')); + $this->assertEquals('/', \OC\Files\Filesystem::normalizePath('/', false)); + $this->assertEquals('/', \OC\Files\Filesystem::normalizePath('//')); + $this->assertEquals('/', \OC\Files\Filesystem::normalizePath('//', false)); $this->assertEquals('/path', \OC\Files\Filesystem::normalizePath('/path/')); $this->assertEquals('/path/', \OC\Files\Filesystem::normalizePath('/path/', false)); $this->assertEquals('/path', \OC\Files\Filesystem::normalizePath('path')); - $this->assertEquals('/path', \OC\Files\Filesystem::normalizePath('\path')); $this->assertEquals('/foo/bar', \OC\Files\Filesystem::normalizePath('/foo//bar/')); + $this->assertEquals('/foo/bar/', \OC\Files\Filesystem::normalizePath('/foo//bar/', false)); $this->assertEquals('/foo/bar', \OC\Files\Filesystem::normalizePath('/foo////bar')); + $this->assertEquals('/foo/bar', \OC\Files\Filesystem::normalizePath('/foo/////bar')); + $this->assertEquals('/foo/bar', \OC\Files\Filesystem::normalizePath('/foo/bar/.')); + $this->assertEquals('/foo/bar', \OC\Files\Filesystem::normalizePath('/foo/bar/./')); + $this->assertEquals('/foo/bar/', \OC\Files\Filesystem::normalizePath('/foo/bar/./', false)); + $this->assertEquals('/foo/bar', \OC\Files\Filesystem::normalizePath('/foo/bar/./.')); + $this->assertEquals('/foo/bar', \OC\Files\Filesystem::normalizePath('/foo/bar/././')); + $this->assertEquals('/foo/bar/', \OC\Files\Filesystem::normalizePath('/foo/bar/././', false)); + $this->assertEquals('/foo/bar', \OC\Files\Filesystem::normalizePath('/foo/./bar/')); + $this->assertEquals('/foo/bar/', \OC\Files\Filesystem::normalizePath('/foo/./bar/', false)); + $this->assertEquals('/foo/.bar', \OC\Files\Filesystem::normalizePath('/foo/.bar/')); + $this->assertEquals('/foo/.bar/', \OC\Files\Filesystem::normalizePath('/foo/.bar/', false)); + $this->assertEquals('/foo/.bar/tee', \OC\Files\Filesystem::normalizePath('/foo/.bar/tee')); + + // normalize does not resolve '..' (by design) + $this->assertEquals('/foo/..', \OC\Files\Filesystem::normalizePath('/foo/../')); + if (class_exists('Patchwork\PHP\Shim\Normalizer')) { $this->assertEquals("/foo/bar\xC3\xBC", \OC\Files\Filesystem::normalizePath("/foo/baru\xCC\x88")); } } + public function testNormalizeWindowsPaths() { + $this->assertEquals('/', \OC\Files\Filesystem::normalizePath('')); + $this->assertEquals('/', \OC\Files\Filesystem::normalizePath('\\')); + $this->assertEquals('/', \OC\Files\Filesystem::normalizePath('\\', false)); + $this->assertEquals('/', \OC\Files\Filesystem::normalizePath('\\\\')); + $this->assertEquals('/', \OC\Files\Filesystem::normalizePath('\\\\', false)); + $this->assertEquals('/path', \OC\Files\Filesystem::normalizePath('\\path\\')); + $this->assertEquals('/path/', \OC\Files\Filesystem::normalizePath('\\path\\', false)); + $this->assertEquals('/foo/bar', \OC\Files\Filesystem::normalizePath('\\foo\\\\bar\\')); + $this->assertEquals('/foo/bar/', \OC\Files\Filesystem::normalizePath('\\foo\\\\bar\\', false)); + $this->assertEquals('/foo/bar', \OC\Files\Filesystem::normalizePath('\\foo\\\\\\\\bar')); + $this->assertEquals('/foo/bar', \OC\Files\Filesystem::normalizePath('\\foo\\\\\\\\\\bar')); + $this->assertEquals('/foo/bar', \OC\Files\Filesystem::normalizePath('\\foo\\bar\\.')); + $this->assertEquals('/foo/bar', \OC\Files\Filesystem::normalizePath('\\foo\\bar\\.\\')); + $this->assertEquals('/foo/bar/', \OC\Files\Filesystem::normalizePath('\\foo\\bar\\.\\', false)); + $this->assertEquals('/foo/bar', \OC\Files\Filesystem::normalizePath('\\foo\\bar\\.\\.')); + $this->assertEquals('/foo/bar', \OC\Files\Filesystem::normalizePath('\\foo\\bar\\.\\.\\')); + $this->assertEquals('/foo/bar/', \OC\Files\Filesystem::normalizePath('\\foo\\bar\\.\\.\\', false)); + $this->assertEquals('/foo/bar', \OC\Files\Filesystem::normalizePath('\\foo\\.\\bar\\')); + $this->assertEquals('/foo/bar/', \OC\Files\Filesystem::normalizePath('\\foo\\.\\bar\\', false)); + $this->assertEquals('/foo/.bar', \OC\Files\Filesystem::normalizePath('\\foo\\.bar\\')); + $this->assertEquals('/foo/.bar/', \OC\Files\Filesystem::normalizePath('\\foo\\.bar\\', false)); + $this->assertEquals('/foo/.bar/tee', \OC\Files\Filesystem::normalizePath('\\foo\\.bar\\tee')); + + // normalize does not resolve '..' (by design) + $this->assertEquals('/foo/..', \OC\Files\Filesystem::normalizePath('\\foo\\..\\')); + + if (class_exists('Patchwork\PHP\Shim\Normalizer')) { + $this->assertEquals("/foo/bar\xC3\xBC", \OC\Files\Filesystem::normalizePath("\\foo\\baru\xCC\x88")); + } + } + public function testHooks() { if(\OC\Files\Filesystem::getView()){ $user = \OC_User::getUser(); From 008c3b80d6cca6a15299afe01f150f075813df8c Mon Sep 17 00:00:00 2001 From: Vincent Petry Date: Thu, 14 Nov 2013 13:22:53 +0100 Subject: [PATCH 2/3] Files app backend now normalizes paths before rendering templates Before rendering breadcrumbs or the file list, the paths are now normalized. This prevents the UI to show "." breadcrumbs in case the path contains sections with "/./" Fixes #5848 --- apps/files/ajax/list.php | 2 +- apps/files/index.php | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/apps/files/ajax/list.php b/apps/files/ajax/list.php index 350fc7fa5f6..0be38c3b96f 100644 --- a/apps/files/ajax/list.php +++ b/apps/files/ajax/list.php @@ -10,7 +10,7 @@ OCP\JSON::checkLoggedIn(); // Load the files $dir = isset( $_GET['dir'] ) ? $_GET['dir'] : ''; - +$dir = \OC\Files\Filesystem::normalizePath($dir); if (!\OC\Files\Filesystem::is_dir($dir . '/')) { header("HTTP/1.0 404 Not Found"); exit(); diff --git a/apps/files/index.php b/apps/files/index.php index f0f95b3bac8..9ae378d7a1d 100644 --- a/apps/files/index.php +++ b/apps/files/index.php @@ -36,6 +36,7 @@ OCP\Util::addscript('files', 'filelist'); OCP\App::setActiveNavigationEntry('files_index'); // Load the files $dir = isset($_GET['dir']) ? stripslashes($_GET['dir']) : ''; +$dir = \OC\Files\Filesystem::normalizePath($dir); // Redirect if directory does not exist if (!\OC\Files\Filesystem::is_dir($dir . '/')) { header('Location: ' . OCP\Util::getScriptName() . ''); @@ -128,7 +129,7 @@ if ($needUpgrade) { $tmpl = new OCP\Template('files', 'index', 'user'); $tmpl->assign('fileList', $list->fetchPage()); $tmpl->assign('breadcrumb', $breadcrumbNav->fetchPage()); - $tmpl->assign('dir', \OC\Files\Filesystem::normalizePath($dir)); + $tmpl->assign('dir', $dir); $tmpl->assign('isCreatable', $isCreatable); $tmpl->assign('permissions', $permissions); $tmpl->assign('files', $files); From d9ab964ff980d495c8960a728fb082f006b6a1e6 Mon Sep 17 00:00:00 2001 From: Vincent Petry Date: Thu, 14 Nov 2013 15:36:03 +0100 Subject: [PATCH 3/3] Added missing test with leading backslash --- tests/lib/files/filesystem.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/lib/files/filesystem.php b/tests/lib/files/filesystem.php index e703a961299..7cb57bf95ad 100644 --- a/tests/lib/files/filesystem.php +++ b/tests/lib/files/filesystem.php @@ -107,6 +107,8 @@ class Filesystem extends \PHPUnit_Framework_TestCase { $this->assertEquals('/', \OC\Files\Filesystem::normalizePath('\\', false)); $this->assertEquals('/', \OC\Files\Filesystem::normalizePath('\\\\')); $this->assertEquals('/', \OC\Files\Filesystem::normalizePath('\\\\', false)); + $this->assertEquals('/path', \OC\Files\Filesystem::normalizePath('\\path')); + $this->assertEquals('/path', \OC\Files\Filesystem::normalizePath('\\path', false)); $this->assertEquals('/path', \OC\Files\Filesystem::normalizePath('\\path\\')); $this->assertEquals('/path/', \OC\Files\Filesystem::normalizePath('\\path\\', false)); $this->assertEquals('/foo/bar', \OC\Files\Filesystem::normalizePath('\\foo\\\\bar\\'));