Improve relative path handling in Template v2 #2310

This commit is contained in:
Kijin Sung 2024-04-30 23:45:17 +09:00
parent 242d07ce30
commit e57c6c5c25
2 changed files with 23 additions and 5 deletions

View file

@ -107,7 +107,7 @@ class Template
} }
// If paths were provided, initialize immediately. // If paths were provided, initialize immediately.
if ($dirname && $filename) if ($dirname !== null && $filename !== null)
{ {
$this->_setSourcePath($dirname, $filename, $extension ?? 'auto'); $this->_setSourcePath($dirname, $filename, $extension ?? 'auto');
} }
@ -137,7 +137,7 @@ class Template
protected function _setSourcePath(string $dirname, string $filename, string $extension = 'auto'): void protected function _setSourcePath(string $dirname, string $filename, string $extension = 'auto'): void
{ {
// Normalize the template path. Result will look like 'modules/foo/views/' // Normalize the template path. Result will look like 'modules/foo/views/'
$dirname = trim(preg_replace('@^' . preg_quote(\RX_BASEDIR, '@') . '|\./@', '', strtr($dirname, ['\\' => '/', '//' => '/'])), '/') . '/'; $dirname = trim(preg_replace('@^(' . preg_quote(\RX_BASEDIR, '@') . '|\./)@', '', strtr($dirname, ['\\' => '/', '//' => '/'])), '/') . '/';
$dirname = preg_replace('/[\{\}\(\)\[\]<>\$\'"]/', '', $dirname); $dirname = preg_replace('/[\{\}\(\)\[\]<>\$\'"]/', '', $dirname);
$this->absolute_dirname = \RX_BASEDIR . $dirname; $this->absolute_dirname = \RX_BASEDIR . $dirname;
$this->relative_dirname = $dirname; $this->relative_dirname = $dirname;
@ -208,7 +208,8 @@ class Template
*/ */
public function setCachePath(?string $cache_path = null) public function setCachePath(?string $cache_path = null)
{ {
$this->cache_path = $cache_path ?? (\RX_BASEDIR . 'files/cache/template/' . $this->relative_path . '.compiled.php'); $clean_path = str_replace('../', '__parentdir/', $this->relative_path);
$this->cache_path = $cache_path ?? (\RX_BASEDIR . 'files/cache/template/' . $clean_path . '.compiled.php');
if ($this->exists) if ($this->exists)
{ {
Debug::addFilenameAlias($this->absolute_path, $this->cache_path); Debug::addFilenameAlias($this->absolute_path, $this->cache_path);
@ -573,7 +574,8 @@ class Template
public function normalizePath(string $path): string public function normalizePath(string $path): string
{ {
$path = preg_replace('#[\\\\/]+#', '/', $path); $path = preg_replace('#[\\\\/]+#', '/', $path);
while (($tmp = preg_replace('#/[^/]+/\.\.(/|$)#', '$1', $path)) !== $path) $path = preg_replace('#/\./#', '/', $path);
while (($tmp = preg_replace('#(/|^)(?!\.\./)[^/]+/\.\.(/|$)#', '$1', $path)) !== $path)
{ {
$path = $tmp; $path = $tmp;
} }

View file

@ -58,11 +58,27 @@ class TemplateTest extends \Codeception\Test\Unit
$tmpl = new \Rhymix\Framework\Template('./tests/_data/template', 'empty.html'); $tmpl = new \Rhymix\Framework\Template('./tests/_data/template', 'empty.html');
$source = '/rhymix/foo/bar//../hello/world\\..'; $source = '/rhymix/foo/bar//../hello/world\\..';
$target = '/rhymix/foo/hello'; $target = '/rhymix/foo/hello/';
$this->assertEquals($target, $tmpl->normalizePath($source)); $this->assertEquals($target, $tmpl->normalizePath($source));
$source = '../foo\\bar/../baz/'; $source = '../foo\\bar/../baz/';
$target = '../foo/baz/'; $target = '../foo/baz/';
$this->assertEquals($target, $tmpl->normalizePath($source)); $this->assertEquals($target, $tmpl->normalizePath($source));
$source = '/fo/ob/ar/../../baz/./buzz.txt';
$target = '/fo/baz/buzz.txt';
$this->assertEquals($target, $tmpl->normalizePath($source));
$source = 'foo/bar/../../baz/buzz.txt';
$target = 'baz/buzz.txt';
$this->assertEquals($target, $tmpl->normalizePath($source));
$source = 'tests/unit/foo/bar/../../../../../../buzz.txt';
$target = '../../buzz.txt';
$this->assertEquals($target, $tmpl->normalizePath($source));
$source = 'tests/unit/foo/bar/../../../../.././';
$target = '../';
$this->assertEquals($target, $tmpl->normalizePath($source));
} }
} }