"Fossies" - the Fresh Open Source Software Archive

Member "moodle/lib/classes/output/mustache_helper_collection.php" (6 Sep 2019, 6959 Bytes) of package /linux/www/moodle-3.6.6.tgz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) PHP source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "mustache_helper_collection.php" see the Fossies "Dox" file reference documentation.

    1 <?php
    2 // This file is part of Moodle - http://moodle.org/
    3 //
    4 // Moodle is free software: you can redistribute it and/or modify
    5 // it under the terms of the GNU General Public License as published by
    6 // the Free Software Foundation, either version 3 of the License, or
    7 // (at your option) any later version.
    8 //
    9 // Moodle is distributed in the hope that it will be useful,
   10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
   11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   12 // GNU General Public License for more details.
   13 //
   14 // You should have received a copy of the GNU General Public License
   15 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
   16 
   17 /**
   18  * Custom Moodle helper collection for mustache.
   19  *
   20  * @copyright  2019 Ryan Wyllie <ryan@moodle.com>
   21  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
   22  */
   23 
   24 namespace core\output;
   25 
   26 /**
   27  * Custom Moodle helper collection for mustache.
   28  *
   29  * @copyright  2019 Ryan Wyllie <ryan@moodle.com>
   30  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
   31  */
   32 class mustache_helper_collection extends \Mustache_HelperCollection {
   33 
   34     /**
   35      * @var string[] Names of helpers that aren't allowed to be called within other helpers.
   36      */
   37     private $blacklistednestedhelpers = [];
   38 
   39     /**
   40      * Helper Collection constructor.
   41      *
   42      * Optionally accepts an array (or Traversable) of `$name => $helper` pairs.
   43      *
   44      * @throws \Mustache_Exception_InvalidArgumentException if the $helpers argument isn't an array or Traversable
   45      *
   46      * @param array|\Traversable $helpers (default: null)
   47      * @param string[] $blacklistednestedhelpers Names of helpers that aren't allowed to be called within other helpers.
   48      */
   49     public function __construct($helpers = null, array $blacklistednestedhelpers = []) {
   50         $this->blacklistednestedhelpers = $blacklistednestedhelpers;
   51         parent::__construct($helpers);
   52     }
   53 
   54     /**
   55      * Add a helper to this collection.
   56      *
   57      * This function has overridden the parent implementation to provide blacklist
   58      * functionality for certain helpers to prevent them being called from within
   59      * other helpers. This is because the JavaScript helper can be used in a
   60      * security exploit if it can be nested.
   61      *
   62      * The function will wrap callable helpers in an anonymous function that strips
   63      * out the blacklisted helpers from the source string before giving it to the
   64      * helper function. This prevents the blacklisted helper functions from being
   65      * called by nested render functions from within other helpers.
   66      *
   67      * @see \Mustache_HelperCollection::add()
   68      * @param string $name
   69      * @param mixed  $helper
   70      */
   71     public function add($name, $helper)
   72     {
   73         $blacklist = $this->blacklistednestedhelpers;
   74 
   75         if (is_callable($helper) && !empty($blacklist)) {
   76             $helper = function($source, \Mustache_LambdaHelper $lambdahelper) use ($helper, $blacklist) {
   77 
   78                 // Temporarily override the blacklisted helpers to return nothing
   79                 // so that they can't be executed from within other helpers.
   80                 $disabledhelpers = $this->disable_helpers($blacklist);
   81                 // Call the original function with the modified sources.
   82                 $result = call_user_func($helper, $source, $lambdahelper);
   83                 // Restore the original blacklisted helper implementations now
   84                 // that this helper has finished executing so that the rest of
   85                 // the rendering process continues to work correctly.
   86                 $this->restore_helpers($disabledhelpers);
   87                 // Lastly parse the returned string to strip out any unwanted helper
   88                 // tags that were added through variable substitution (or other means).
   89                 // This is done because a secondary render is called on the result
   90                 // of a helper function if it still includes mustache tags. See
   91                 // the section function of Mustache_Compiler for details.
   92                 return $this->strip_blacklisted_helpers($blacklist, $result);
   93             };
   94         }
   95 
   96         parent::add($name, $helper);
   97     }
   98 
   99     /**
  100      * Disable a list of helpers (by name) by changing their implementation to
  101      * simply return an empty string.
  102      *
  103      * @param  string[] $names List of helper names to disable
  104      * @return \Closure[] The original helper functions indexed by name
  105      */
  106     private function disable_helpers($names) {
  107         $disabledhelpers = [];
  108 
  109         foreach ($names as $name) {
  110             if ($this->has($name)) {
  111                 $function = $this->get($name);
  112                 // Null out the helper. Must call parent::add here to avoid
  113                 // a recursion problem.
  114                 parent::add($name, function() {
  115                     return '';
  116                 });
  117 
  118                 $disabledhelpers[$name] = $function;
  119             }
  120         }
  121 
  122         return $disabledhelpers;
  123     }
  124 
  125     /**
  126      * Restore the original helper implementations. Typically used after disabling
  127      * a helper.
  128      *
  129      * @param  \Closure[] $helpers The helper functions indexed by name
  130      */
  131     private function restore_helpers($helpers) {
  132         foreach ($helpers as $name => $function) {
  133             // Restore the helper functions. Must call parent::add here to avoid
  134             // a recursion problem.
  135             parent::add($name, $function);
  136         }
  137     }
  138 
  139     /**
  140      * Parse the given string and remove any reference to blacklisted helpers.
  141      *
  142      * E.g.
  143      * $blacklist = ['js'];
  144      * $string = "core, move, {{#js}} some nasty JS hack {{/js}}"
  145      * result: "core, move, {{}}"
  146      *
  147      * @param  string[] $blacklist List of helper names to strip
  148      * @param  string $string String to parse
  149      * @return string Parsed string
  150      */
  151     public function strip_blacklisted_helpers($blacklist, $string) {
  152         $starttoken = \Mustache_Tokenizer::T_SECTION;
  153         $endtoken = \Mustache_Tokenizer::T_END_SECTION;
  154         if ($endtoken == '/') {
  155             $endtoken = '\/';
  156         }
  157 
  158         $regexes = array_map(function($name) use ($starttoken, $endtoken) {
  159             // We only strip out the name of the helper (excluding delimiters)
  160             // the user is able to change the delimeters on a per template
  161             // basis so they may not be curly braces.
  162             return '/\s*' . $starttoken . '\s*'. $name . '\W+.*' . $endtoken . '\s*' . $name . '\s*/';
  163         }, $blacklist);
  164 
  165         // This will strip out unwanted helpers from the $source string
  166         // before providing it to the original helper function.
  167         // E.g.
  168         // Before:
  169         // "core, move, {{#js}} some nasty JS hack {{/js}}"
  170         // After:
  171         // "core, move, {{}}"
  172         return preg_replace_callback($regexes, function() {
  173             return '';
  174         }, $string);
  175     }
  176 }