Fix path normalization problem

This commit is contained in:
Kijin Sung 2023-10-21 14:19:20 +09:00
parent 6085b82d19
commit fefd3dd895
8 changed files with 62 additions and 18 deletions

View file

@ -492,10 +492,20 @@ class Template
$path = preg_replace('#/\./#', '/', $basepath . $path);
}
// Remove extra slashes and parent directory references.
$path = preg_replace('#\\\\#', '/', $path);
$path = preg_replace('#//#', '/', $path);
while (($tmp = preg_replace('#/[^/]+/\.\./#', '/', $path)) !== $path)
// Normalize and return the path.
return $this->normalizePath($path);
}
/**
* Normalize a path by removing extra slashes and parent directory references.
*
* @param string $path
* @return string
*/
public function normalizePath(string $path): string
{
$path = preg_replace('#[\\\\/]+#', '/', $path);
while (($tmp = preg_replace('#/[^/]+/\.\.(/|$)#', '$1', $path)) !== $path)
{
$path = $tmp;
}
@ -527,13 +537,6 @@ class Template
$path = $isConditional ? $args[2] : $args[1];
$vars = $isConditional ? ($args[3] ?? null) : ($args[2] ?? null);
// Handle paths relative to the Rhymix installation directory.
if (preg_match('#^\^/?(\w.+)$#s', $path, $match))
{
$basedir = str_contains($match[1], '/') ? dirname($match[1]) : \RX_BASEDIR;
$path = basename($match[1]);
}
// If the conditions are not met, return.
if ($isConditional && $directive === 'includeWhen' && !$cond)
{
@ -544,6 +547,20 @@ class Template
return '';
}
// Handle paths relative to the Rhymix installation directory.
if (preg_match('#^\^/?(\w.+)$#s', $path, $match))
{
$basedir = str_contains($match[1], '/') ? dirname($match[1]) : \RX_BASEDIR;
$path = basename($match[1]);
}
// Convert relative paths embedded in the filename.
if (preg_match('#^(.+)/([^/]+)$#', $path, $match))
{
$basedir = $this->normalizePath($basedir . $match[1] . '/');
$path = $match[2];
}
// Create a new instance of TemplateHandler.
$template = new self($basedir, $path, $extension);

View file

@ -399,6 +399,11 @@ class TemplateParser_v2
$dir = '"' . (str_contains($m[1], '/') ? dirname($m[1]) : '') . '"';
$path = basename($m[1]);
}
if (preg_match('#^(.+)/([^/]+)$#', $path, $match))
{
$dir = '$this->normalizePath(' . $dir . ' . "' . $match[1] . '")';
$path = $match[2];
}
// Generate the code to create a new Template object and compile it.
$tpl = '<?php $__tpl = new \Rhymix\Framework\Template(' . $dir . ', "' . $path . '", "' . ($this->template->extension ?: 'auto') . '"); ';

View file

@ -1,3 +1,6 @@
<config version="2" />
<div>{{ $foobar }}</div>
<div>{{ $globalonly }}</div>
@once
@load ('../js/test.js')
@endonce

View file

@ -1,5 +1,5 @@
<config version="2" />
<div class="self">{{ $foobar }}</div>
<div class="incl">
<include src="incl/scopetest1.html" vars="['foobar' => 'Included #3']" />
<include src="scopetest1.html" vars="['foobar' => 'Included #3']" />
</div>

View file

@ -0,0 +1,3 @@
(function($) {
// TEST
})(jQuery);

View file

@ -1,9 +1,9 @@
@version(2)
@php
<?php
$foobar = 'Rhymix Template';
$globalonly = 'Context Variable';
@endphp
?>
<div class="global">
@include ('incl/scopetest1.html')
@ -14,5 +14,5 @@
</div>
<div class="test2">
@include ('incl/scopetest2.html', ['foobar' => 'Included #2'])
<include src="incl/scopetest2.html" vars="['foobar' => 'Included #2']" />
</div>

View file

@ -51,4 +51,17 @@ class TemplateTest extends \Codeception\Test\Unit
$target = '/rhymix/foo/bar.gif';
$this->assertEquals($target, $tmpl->convertPath($source));
}
public function testNormalizePath()
{
$tmpl = new \Rhymix\Framework\Template('./tests/_data/template', 'empty.html');
$source = '/rhymix/foo/bar//../hello/world\\..';
$target = '/rhymix/foo/hello';
$this->assertEquals($target, $tmpl->normalizePath($source));
$source = '../foo\\bar/../baz/';
$target = '../foo/baz/';
$this->assertEquals($target, $tmpl->normalizePath($source));
}
}

View file

@ -55,17 +55,17 @@ class TemplateParserV2Test extends \Codeception\Test\Unit
// Legacy 'target' attribute
$source = '<include target="subdir/foobar" />';
$target = '<?php $__tpl = new \Rhymix\Framework\Template($this->relative_dirname, "subdir/foobar", "html"); if ($this->vars): $__tpl->setVars($this->vars); endif; echo $__tpl->compile(); ?>';
$target = '<?php $__tpl = new \Rhymix\Framework\Template($this->normalizePath($this->relative_dirname . "subdir"), "foobar", "html"); if ($this->vars): $__tpl->setVars($this->vars); endif; echo $__tpl->compile(); ?>';
$this->assertEquals($target, $this->_parse($source));
// Conditional include
$source = '<include src="../up/foobar" if="$cond" />';
$target = '<?php if(!empty($cond)): ?><?php $__tpl = new \Rhymix\Framework\Template($this->relative_dirname, "../up/foobar", "html"); if ($this->vars): $__tpl->setVars($this->vars); endif; echo $__tpl->compile(); ?><?php endif; ?>';
$target = '<?php if(!empty($cond)): ?><?php $__tpl = new \Rhymix\Framework\Template($this->normalizePath($this->relative_dirname . "../up"), "foobar", "html"); if ($this->vars): $__tpl->setVars($this->vars); endif; echo $__tpl->compile(); ?><?php endif; ?>';
$this->assertEquals($target, $this->_parse($source));
// Conditional include with legacy 'cond' attribute
$source = '<include target="legacy/cond.statement.html" cond="$cond" />';
$target = '<?php if(!empty($cond)): ?><?php $__tpl = new \Rhymix\Framework\Template($this->relative_dirname, "legacy/cond.statement.html", "html"); if ($this->vars): $__tpl->setVars($this->vars); endif; echo $__tpl->compile(); ?><?php endif; ?>';
$target = '<?php if(!empty($cond)): ?><?php $__tpl = new \Rhymix\Framework\Template($this->normalizePath($this->relative_dirname . "legacy"), "cond.statement.html", "html"); if ($this->vars): $__tpl->setVars($this->vars); endif; echo $__tpl->compile(); ?><?php endif; ?>';
$this->assertEquals($target, $this->_parse($source));
// Path relative to Rhymix installation directory
@ -1183,6 +1183,9 @@ class TemplateParserV2Test extends \Codeception\Test\Unit
$this->_normalizeWhitespace($expected),
$this->_normalizeWhitespace($executed_output)
);
$list = \Context::getJsFile();
$this->assertStringContainsString('/rhymix/tests/_data/template/js/test.js', array_last($list)['file']);
}
/**