Skip to content

Commit

Permalink
MDL-68066 output: Mustache - Add option to disable lambda rendering
Browse files Browse the repository at this point in the history
This commit is based on an outstanding pull request to the Mustache repo
  • Loading branch information
larsbonczek authored and junpataleta committed Sep 7, 2022
1 parent c4df60b commit 013cb2f
Show file tree
Hide file tree
Showing 2 changed files with 73 additions and 35 deletions.
79 changes: 53 additions & 26 deletions lib/mustache/src/Mustache/Compiler.php
Expand Up @@ -26,31 +26,34 @@ class Mustache_Compiler
private $entityFlags;
private $charset;
private $strictCallables;
private $disableLambdaRendering;

/**
* Compile a Mustache token parse tree into PHP source code.
*
* @param string $source Mustache Template source code
* @param string $tree Parse tree of Mustache tokens
* @param string $name Mustache Template class name
* @param bool $customEscape (default: false)
* @param string $charset (default: 'UTF-8')
* @param bool $strictCallables (default: false)
* @param int $entityFlags (default: ENT_COMPAT)
* @param string $source Mustache Template source code
* @param array $tree Parse tree of Mustache tokens
* @param string $name Mustache Template class name
* @param bool $customEscape (default: false)
* @param string $charset (default: 'UTF-8')
* @param bool $strictCallables (default: false)
* @param int $entityFlags (default: ENT_COMPAT)
* @param bool $disableLambdaRendering (default: false)
*
* @return string Generated PHP source code
*/
public function compile($source, array $tree, $name, $customEscape = false, $charset = 'UTF-8', $strictCallables = false, $entityFlags = ENT_COMPAT)
public function compile($source, array $tree, $name, $customEscape = false, $charset = 'UTF-8', $strictCallables = false, $entityFlags = ENT_COMPAT, $disableLambdaRendering = false)
{
$this->pragmas = $this->defaultPragmas;
$this->sections = array();
$this->blocks = array();
$this->source = $source;
$this->indentNextLine = true;
$this->customEscape = $customEscape;
$this->entityFlags = $entityFlags;
$this->charset = $charset;
$this->strictCallables = $strictCallables;
$this->pragmas = $this->defaultPragmas;
$this->sections = array();
$this->blocks = array();
$this->source = $source;
$this->indentNextLine = true;
$this->customEscape = $customEscape;
$this->entityFlags = $entityFlags;
$this->charset = $charset;
$this->strictCallables = $strictCallables;
$this->disableLambdaRendering = $disableLambdaRendering;

return $this->writeCode($tree, $name);
}
Expand Down Expand Up @@ -331,14 +334,8 @@ private function section%s(Mustache_Context $context, $indent, $value)
if (%s) {
$source = %s;
$result = (string) call_user_func($value, $source, %s);
if (strpos($result, \'{{\') === false) {
$buffer .= $result;
} else {
$buffer .= $this->mustache
->loadLambda($result%s)
->renderInternal($context);
}
$result = (string) call_user_func($value, $source, %s);%s
$buffer .= $result;
} elseif (!empty($value)) {
$values = $this->isIterable($value) ? $value : array($value);
foreach ($values as $value) {
Expand All @@ -352,6 +349,36 @@ private function section%s(Mustache_Context $context, $indent, $value)
}
';

const SECTION_RENDER_LAMBDA = '
if (strpos($result, \'{{\') !== false) {
$result = $this->mustache
->loadLambda($result%s)
->renderInternal($context);
}
';

/**
* Helper function to compile section with and without lambda rendering.
*
* @param string $key
* @param string $callable
* @param string $source
* @param string $helper
* @param string $delims
* @param string $content
*
* @return string section code
*/
private function getSection($key, $callable, $source, $helper, $delims, $content)
{
$render = '';
if (!$this->disableLambdaRendering) {
$render = sprintf($this->prepare(self::SECTION_RENDER_LAMBDA, 2), $delims);
}

return sprintf($this->prepare(self::SECTION), $key, $callable, $source, $helper, $render, $content);
}

/**
* Generate Mustache Template section PHP source.
*
Expand Down Expand Up @@ -383,7 +410,7 @@ private function section($nodes, $id, $filters, $start, $end, $otag, $ctag, $lev
$key = ucfirst(md5($delims . "\n" . $source));

if (!isset($this->sections[$key])) {
$this->sections[$key] = sprintf($this->prepare(self::SECTION), $key, $callable, $source, $helper, $delims, $this->walk($nodes, 2));
$this->sections[$key] = $this->getSection($key, $callable, $source, $helper, $delims, $this->walk($nodes, 2));
}

$method = $this->getFindMethod($id);
Expand Down
29 changes: 20 additions & 9 deletions lib/mustache/src/Mustache/Engine.php
Expand Up @@ -53,6 +53,7 @@ class Mustache_Engine
private $charset = 'UTF-8';
private $logger;
private $strictCallables = false;
private $disableLambdaRendering = false;
private $pragmas = array();
private $delimiters;

Expand Down Expand Up @@ -130,6 +131,11 @@ class Mustache_Engine
* // This currently defaults to false, but will default to true in v3.0.
* 'strict_callables' => true,
*
* // Do not render the output of lambdas. Use this to prevent repeated rendering if the lambda already
* // takes care of rendering its content. This helps protect against mustache code injection when user
* // input is passed directly into the template. Defaults to false.
* 'disable_lambda_rendering' => true,
*
* // Enable pragmas across all templates, regardless of the presence of pragma tags in the individual
* // templates.
* 'pragmas' => [Mustache_Engine::PRAGMA_FILTERS],
Expand Down Expand Up @@ -204,6 +210,10 @@ public function __construct(array $options = array())
$this->strictCallables = $options['strict_callables'];
}

if (isset($options['disable_lambda_rendering'])) {
$this->disableLambdaRendering = $options['disable_lambda_rendering'];
}

if (isset($options['delimiters'])) {
$this->delimiters = $options['delimiters'];
}
Expand Down Expand Up @@ -624,14 +634,15 @@ public function getTemplateClassName($source)
//
// Keep this list in alphabetical order :)
$chunks = array(
'charset' => $this->charset,
'delimiters' => $this->delimiters ? $this->delimiters : '{{ }}',
'entityFlags' => $this->entityFlags,
'escape' => isset($this->escape) ? 'custom' : 'default',
'key' => ($source instanceof Mustache_Source) ? $source->getKey() : 'source',
'pragmas' => $this->getPragmas(),
'strictCallables' => $this->strictCallables,
'version' => self::VERSION,
'charset' => $this->charset,
'delimiters' => $this->delimiters ? $this->delimiters : '{{ }}',
'entityFlags' => $this->entityFlags,
'escape' => isset($this->escape) ? 'custom' : 'default',
'key' => ($source instanceof Mustache_Source) ? $source->getKey() : 'source',
'pragmas' => $this->getPragmas(),
'strictCallables' => $this->strictCallables,
'disableLambdaRendering' => $this->disableLambdaRendering,
'version' => self::VERSION,
);

$key = json_encode($chunks);
Expand Down Expand Up @@ -810,7 +821,7 @@ private function compile($source)
$compiler = $this->getCompiler();
$compiler->setPragmas($this->getPragmas());

return $compiler->compile($source, $tree, $name, isset($this->escape), $this->charset, $this->strictCallables, $this->entityFlags);
return $compiler->compile($source, $tree, $name, isset($this->escape), $this->charset, $this->strictCallables, $this->entityFlags, $this->disableLambdaRendering);
}

/**
Expand Down

0 comments on commit 013cb2f

Please sign in to comment.