mirror of
https://github.com/smarty-php/smarty.git
synced 2025-08-02 09:24:28 +02:00
Merge pull request from GHSA-4rmg-292m-wg3w
This commit is contained in:
2
changelog/GHSA-4rmg-292m-wg3w.md
Normal file
2
changelog/GHSA-4rmg-292m-wg3w.md
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
- Fixed a code injection vulnerability in extends-tag. This addresses CVE-2024-35226.
|
||||||
|
- Added `$smarty->setCacheModifiedCheck()` setter for cache_modified_check
|
@@ -32,7 +32,7 @@ class ExtendsTag extends Inheritance {
|
|||||||
*
|
*
|
||||||
* @var array
|
* @var array
|
||||||
*/
|
*/
|
||||||
protected $optional_attributes = ['extends_resource'];
|
protected $optional_attributes = [];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Attribute definition: Overwrites base class.
|
* Attribute definition: Overwrites base class.
|
||||||
@@ -64,29 +64,7 @@ class ExtendsTag extends Inheritance {
|
|||||||
}
|
}
|
||||||
// add code to initialize inheritance
|
// add code to initialize inheritance
|
||||||
$this->registerInit($compiler, true);
|
$this->registerInit($compiler, true);
|
||||||
$file = trim($_attr['file'], '\'"');
|
$this->compileEndChild($compiler, $_attr['file']);
|
||||||
if (strlen($file) > 8 && substr($file, 0, 8) === 'extends:') {
|
|
||||||
// generate code for each template
|
|
||||||
$files = array_reverse(explode('|', substr($file, 8)));
|
|
||||||
$i = 0;
|
|
||||||
foreach ($files as $file) {
|
|
||||||
if ($file[0] === '"') {
|
|
||||||
$file = trim($file, '".');
|
|
||||||
} else {
|
|
||||||
$file = "'{$file}'";
|
|
||||||
}
|
|
||||||
$i++;
|
|
||||||
if ($i === count($files) && isset($_attr['extends_resource'])) {
|
|
||||||
$this->compileEndChild($compiler);
|
|
||||||
}
|
|
||||||
$this->compileInclude($compiler, $file);
|
|
||||||
}
|
|
||||||
if (!isset($_attr['extends_resource'])) {
|
|
||||||
$this->compileEndChild($compiler);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$this->compileEndChild($compiler, $_attr['file']);
|
|
||||||
}
|
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -106,42 +84,4 @@ class ExtendsTag extends Inheritance {
|
|||||||
(isset($template) ? ", {$template}, \$_smarty_current_dir" : '') . ");\n?>"
|
(isset($template) ? ", {$template}, \$_smarty_current_dir" : '') . ");\n?>"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Add code for including subtemplate to end of template
|
|
||||||
*
|
|
||||||
* @param \Smarty\Compiler\Template $compiler
|
|
||||||
* @param string $template subtemplate name
|
|
||||||
*
|
|
||||||
* @throws \Smarty\CompilerException
|
|
||||||
* @throws \Smarty\Exception
|
|
||||||
*/
|
|
||||||
private function compileInclude(\Smarty\Compiler\Template $compiler, $template) {
|
|
||||||
$compiler->getParser()->template_postfix[] = new \Smarty\ParseTree\Tag(
|
|
||||||
$compiler->getParser(),
|
|
||||||
$compiler->compileTag(
|
|
||||||
'include',
|
|
||||||
[
|
|
||||||
$template,
|
|
||||||
['scope' => 'parent'],
|
|
||||||
]
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create source code for {extends} from source components array
|
|
||||||
*
|
|
||||||
* @param \Smarty\Template $template
|
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public static function extendsSourceArrayCode(\Smarty\Template $template) {
|
|
||||||
$resources = [];
|
|
||||||
foreach ($template->getSource()->components as $source) {
|
|
||||||
$resources[] = $source->resource;
|
|
||||||
}
|
|
||||||
return $template->getLeftDelimiter() . 'extends file=\'extends:' . join('|', $resources) .
|
|
||||||
'\' extends_resource=true' . $template->getRightDelimiter();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@@ -403,21 +403,37 @@ class Template extends BaseCompiler {
|
|||||||
}
|
}
|
||||||
// get template source
|
// get template source
|
||||||
if (!empty($this->template->getSource()->components)) {
|
if (!empty($this->template->getSource()->components)) {
|
||||||
// we have array of inheritance templates by extends: resource
|
|
||||||
// generate corresponding source code sequence
|
$_compiled_code = '<?php $_smarty_tpl->getInheritance()->init($_smarty_tpl, true); ?>';
|
||||||
$_content =
|
|
||||||
ExtendsTag::extendsSourceArrayCode($this->template);
|
$i = 0;
|
||||||
|
$reversed_components = array_reverse($this->template->getSource()->components);
|
||||||
|
foreach ($reversed_components as $source) {
|
||||||
|
$i++;
|
||||||
|
if ($i === count($reversed_components)) {
|
||||||
|
$_compiled_code .= '<?php $_smarty_tpl->getInheritance()->endChild($_smarty_tpl); ?>';
|
||||||
|
}
|
||||||
|
$_compiled_code .= $this->compileTag(
|
||||||
|
'include',
|
||||||
|
[
|
||||||
|
var_export($source->resource, true),
|
||||||
|
['scope' => 'parent'],
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
$_compiled_code = $this->smarty->runPostFilters($_compiled_code, $this->template);
|
||||||
} else {
|
} else {
|
||||||
// get template source
|
// get template source
|
||||||
$_content = $this->template->getSource()->getContent();
|
$_content = $this->template->getSource()->getContent();
|
||||||
|
$_compiled_code = $this->smarty->runPostFilters(
|
||||||
|
$this->doCompile(
|
||||||
|
$this->smarty->runPreFilters($_content, $this->template),
|
||||||
|
true
|
||||||
|
),
|
||||||
|
$this->template
|
||||||
|
);
|
||||||
}
|
}
|
||||||
$_compiled_code = $this->smarty->runPostFilters(
|
|
||||||
$this->doCompile(
|
|
||||||
$this->smarty->runPreFilters($_content, $this->template),
|
|
||||||
true
|
|
||||||
),
|
|
||||||
$this->template
|
|
||||||
);
|
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
if ($this->smarty->debugging) {
|
if ($this->smarty->debugging) {
|
||||||
$this->smarty->getDebug()->end_compile($this->template);
|
$this->smarty->getDebug()->end_compile($this->template);
|
||||||
|
@@ -2211,5 +2211,14 @@ class Smarty extends \Smarty\TemplateBase {
|
|||||||
return $template;
|
return $template;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets if Smarty should check If-Modified-Since headers to determine cache validity.
|
||||||
|
* @param bool $cache_modified_check
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function setCacheModifiedCheck($cache_modified_check): void {
|
||||||
|
$this->cache_modified_check = (bool) $cache_modified_check;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1193,8 +1193,38 @@ class CompileBlockExtendsTest extends PHPUnit_Smarty
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testBlockWithAssign() {
|
public function testBlockWithAssign() {
|
||||||
$this->assertEquals('Captured content is: Content with lots of html here', $this->smarty->fetch('038_child.tpl'));
|
$this->assertEquals('Captured content is: Content with lots of html here', $this->smarty->fetch('038_child.tpl'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test escaping of file parameter
|
||||||
|
*/
|
||||||
|
public function testEscaping()
|
||||||
|
{
|
||||||
|
$this->expectException(\Smarty\Exception::class);
|
||||||
|
$this->expectExceptionMessageMatches('/Unable to load.*/');
|
||||||
|
$this->assertEquals('hello world', $this->smarty->fetch('escaping.tpl'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test escaping of file parameter 2
|
||||||
|
*/
|
||||||
|
public function testEscaping2()
|
||||||
|
{
|
||||||
|
$this->expectException(\Smarty\Exception::class);
|
||||||
|
$this->expectExceptionMessageMatches('/Unable to load.*/');
|
||||||
|
$this->assertEquals('hello world', $this->smarty->fetch('escaping2.tpl'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test escaping of file parameter 3
|
||||||
|
*/
|
||||||
|
public function testEscaping3()
|
||||||
|
{
|
||||||
|
$this->expectException(\Smarty\Exception::class);
|
||||||
|
$this->expectExceptionMessageMatches('/Unable to load.*/');
|
||||||
|
$this->assertEquals('hello world', $this->smarty->fetch('escaping3.tpl'));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -0,0 +1 @@
|
|||||||
|
{extends "extends:helloworld.tpl', var_dump(shell_exec('ls')), 1, 2, 3);}}?>"}
|
@@ -0,0 +1 @@
|
|||||||
|
{extends 'extends:"helloworld.tpl\', var_dump(shell_exec(\'ls\')), 1, 2, 3);}}?>'}
|
@@ -0,0 +1 @@
|
|||||||
|
{extends file='extends:"helloworld.tpl'|cat:"', var_dump(shell_exec('ls')), 1, 2, 3);}}?>"}
|
@@ -82,6 +82,18 @@ class CompileIncludeTest extends PHPUnit_Smarty
|
|||||||
$this->assertEquals('I1I2I3', $content, $text);
|
$this->assertEquals('I1I2I3', $content, $text);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* test template name escaping
|
||||||
|
*/
|
||||||
|
public function testIncludeFilenameEscaping()
|
||||||
|
{
|
||||||
|
$this->expectException(\Smarty\Exception::class);
|
||||||
|
$this->expectExceptionMessageMatches('/Unable to load.*/');
|
||||||
|
$tpl = $this->smarty->createTemplate('test_include_security.tpl');
|
||||||
|
$content = $this->smarty->fetch($tpl);
|
||||||
|
$this->assertEquals("hello world", $content);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* test standard output
|
* test standard output
|
||||||
*
|
*
|
||||||
|
@@ -0,0 +1 @@
|
|||||||
|
{include file="helloworld.tpl', var_dump(shell_exec('ls')), 1, 2, 3);}}?>"}
|
@@ -32,4 +32,11 @@ class ExtendsIssue419Test extends PHPUnit_Smarty
|
|||||||
$this->assertEquals('child', $this->smarty->fetch('extends:001_parent.tpl|001_child.tpl'));
|
$this->assertEquals('child', $this->smarty->fetch('extends:001_parent.tpl|001_child.tpl'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testextendsSecurity()
|
||||||
|
{
|
||||||
|
$this->expectException(\Smarty\Exception::class);
|
||||||
|
$this->expectExceptionMessageMatches('/Unable to load.*/');
|
||||||
|
$this->assertEquals('child', $this->smarty->fetch('string:{include "001_parent.tpl\', var_dump(shell_exec(\'ls\')), 1, 2, 3);}}?>"}'));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user