Skip to content

Commit 0be92bc

Browse files
authored
Merge pull request from GHSA-4rmg-292m-wg3w
1 parent 61db287 commit 0be92bc

11 files changed

Lines changed: 96 additions & 76 deletions

File tree

changelog/GHSA-4rmg-292m-wg3w.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
- Fixed a code injection vulnerability in extends-tag. This addresses CVE-2024-35226.
2+
- Added `$smarty->setCacheModifiedCheck()` setter for cache_modified_check

src/Compile/Tag/ExtendsTag.php

Lines changed: 2 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ class ExtendsTag extends Inheritance {
3232
*
3333
* @var array
3434
*/
35-
protected $optional_attributes = ['extends_resource'];
35+
protected $optional_attributes = [];
3636

3737
/**
3838
* Attribute definition: Overwrites base class.
@@ -64,29 +64,7 @@ public function compile($args, \Smarty\Compiler\Template $compiler, $parameter =
6464
}
6565
// add code to initialize inheritance
6666
$this->registerInit($compiler, true);
67-
$file = trim($_attr['file'], '\'"');
68-
if (strlen($file) > 8 && substr($file, 0, 8) === 'extends:') {
69-
// generate code for each template
70-
$files = array_reverse(explode('|', substr($file, 8)));
71-
$i = 0;
72-
foreach ($files as $file) {
73-
if ($file[0] === '"') {
74-
$file = trim($file, '".');
75-
} else {
76-
$file = "'{$file}'";
77-
}
78-
$i++;
79-
if ($i === count($files) && isset($_attr['extends_resource'])) {
80-
$this->compileEndChild($compiler);
81-
}
82-
$this->compileInclude($compiler, $file);
83-
}
84-
if (!isset($_attr['extends_resource'])) {
85-
$this->compileEndChild($compiler);
86-
}
87-
} else {
88-
$this->compileEndChild($compiler, $_attr['file']);
89-
}
67+
$this->compileEndChild($compiler, $_attr['file']);
9068
return '';
9169
}
9270

@@ -106,42 +84,4 @@ private function compileEndChild(\Smarty\Compiler\Template $compiler, $template
10684
(isset($template) ? ", {$template}, \$_smarty_current_dir" : '') . ");\n?>"
10785
);
10886
}
109-
110-
/**
111-
* Add code for including subtemplate to end of template
112-
*
113-
* @param \Smarty\Compiler\Template $compiler
114-
* @param string $template subtemplate name
115-
*
116-
* @throws \Smarty\CompilerException
117-
* @throws \Smarty\Exception
118-
*/
119-
private function compileInclude(\Smarty\Compiler\Template $compiler, $template) {
120-
$compiler->getParser()->template_postfix[] = new \Smarty\ParseTree\Tag(
121-
$compiler->getParser(),
122-
$compiler->compileTag(
123-
'include',
124-
[
125-
$template,
126-
['scope' => 'parent'],
127-
]
128-
)
129-
);
130-
}
131-
132-
/**
133-
* Create source code for {extends} from source components array
134-
*
135-
* @param \Smarty\Template $template
136-
*
137-
* @return string
138-
*/
139-
public static function extendsSourceArrayCode(\Smarty\Template $template) {
140-
$resources = [];
141-
foreach ($template->getSource()->components as $source) {
142-
$resources[] = $source->resource;
143-
}
144-
return $template->getLeftDelimiter() . 'extends file=\'extends:' . join('|', $resources) .
145-
'\' extends_resource=true' . $template->getRightDelimiter();
146-
}
14787
}

src/Compiler/Template.php

Lines changed: 27 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -403,21 +403,37 @@ public function compileTemplateSource(\Smarty\Template $template, \Smarty\Compil
403403
}
404404
// get template source
405405
if (!empty($this->template->getSource()->components)) {
406-
// we have array of inheritance templates by extends: resource
407-
// generate corresponding source code sequence
408-
$_content =
409-
ExtendsTag::extendsSourceArrayCode($this->template);
406+
407+
$_compiled_code = '<?php $_smarty_tpl->getInheritance()->init($_smarty_tpl, true); ?>';
408+
409+
$i = 0;
410+
$reversed_components = array_reverse($this->template->getSource()->components);
411+
foreach ($reversed_components as $source) {
412+
$i++;
413+
if ($i === count($reversed_components)) {
414+
$_compiled_code .= '<?php $_smarty_tpl->getInheritance()->endChild($_smarty_tpl); ?>';
415+
}
416+
$_compiled_code .= $this->compileTag(
417+
'include',
418+
[
419+
var_export($source->resource, true),
420+
['scope' => 'parent'],
421+
]
422+
);
423+
}
424+
$_compiled_code = $this->smarty->runPostFilters($_compiled_code, $this->template);
410425
} else {
411426
// get template source
412427
$_content = $this->template->getSource()->getContent();
428+
$_compiled_code = $this->smarty->runPostFilters(
429+
$this->doCompile(
430+
$this->smarty->runPreFilters($_content, $this->template),
431+
true
432+
),
433+
$this->template
434+
);
413435
}
414-
$_compiled_code = $this->smarty->runPostFilters(
415-
$this->doCompile(
416-
$this->smarty->runPreFilters($_content, $this->template),
417-
true
418-
),
419-
$this->template
420-
);
436+
421437
} catch (\Exception $e) {
422438
if ($this->smarty->debugging) {
423439
$this->smarty->getDebug()->end_compile($this->template);

src/Smarty.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2211,5 +2211,14 @@ private function returnOrCreateTemplate($template, $cache_id = null, $compile_id
22112211
return $template;
22122212
}
22132213

2214+
/**
2215+
* Sets if Smarty should check If-Modified-Since headers to determine cache validity.
2216+
* @param bool $cache_modified_check
2217+
* @return void
2218+
*/
2219+
public function setCacheModifiedCheck($cache_modified_check): void {
2220+
$this->cache_modified_check = (bool) $cache_modified_check;
2221+
}
2222+
22142223
}
22152224

tests/UnitTests/TemplateSource/TagTests/BockExtend/CompileBlockExtendsTest.php

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1193,8 +1193,38 @@ public function dataTestBlockNocache()
11931193
);
11941194
}
11951195

1196-
public function testBlockWithAssign() {
1197-
$this->assertEquals('Captured content is: Content with lots of html here', $this->smarty->fetch('038_child.tpl'));
1198-
}
1196+
public function testBlockWithAssign() {
1197+
$this->assertEquals('Captured content is: Content with lots of html here', $this->smarty->fetch('038_child.tpl'));
1198+
}
1199+
1200+
/**
1201+
* Test escaping of file parameter
1202+
*/
1203+
public function testEscaping()
1204+
{
1205+
$this->expectException(\Smarty\Exception::class);
1206+
$this->expectExceptionMessageMatches('/Unable to load.*/');
1207+
$this->assertEquals('hello world', $this->smarty->fetch('escaping.tpl'));
1208+
}
1209+
1210+
/**
1211+
* Test escaping of file parameter 2
1212+
*/
1213+
public function testEscaping2()
1214+
{
1215+
$this->expectException(\Smarty\Exception::class);
1216+
$this->expectExceptionMessageMatches('/Unable to load.*/');
1217+
$this->assertEquals('hello world', $this->smarty->fetch('escaping2.tpl'));
1218+
}
1219+
1220+
/**
1221+
* Test escaping of file parameter 3
1222+
*/
1223+
public function testEscaping3()
1224+
{
1225+
$this->expectException(\Smarty\Exception::class);
1226+
$this->expectExceptionMessageMatches('/Unable to load.*/');
1227+
$this->assertEquals('hello world', $this->smarty->fetch('escaping3.tpl'));
1228+
}
11991229

12001230
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{extends "extends:helloworld.tpl', var_dump(shell_exec('ls')), 1, 2, 3);}}?>"}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{extends 'extends:"helloworld.tpl\', var_dump(shell_exec(\'ls\')), 1, 2, 3);}}?>'}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{extends file='extends:"helloworld.tpl'|cat:"', var_dump(shell_exec('ls')), 1, 2, 3);}}?>"}

tests/UnitTests/TemplateSource/TagTests/Include/CompileIncludeTest.php

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,18 @@ public function testSpacing_001V3($merge, $caching, $text)
8282
$this->assertEquals('I1I2I3', $content, $text);
8383
}
8484

85+
/**
86+
* test template name escaping
87+
*/
88+
public function testIncludeFilenameEscaping()
89+
{
90+
$this->expectException(\Smarty\Exception::class);
91+
$this->expectExceptionMessageMatches('/Unable to load.*/');
92+
$tpl = $this->smarty->createTemplate('test_include_security.tpl');
93+
$content = $this->smarty->fetch($tpl);
94+
$this->assertEquals("hello world", $content);
95+
}
96+
8597
/**
8698
* test standard output
8799
*
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{include file="helloworld.tpl', var_dump(shell_exec('ls')), 1, 2, 3);}}?>"}

0 commit comments

Comments
 (0)