Completed
Branch master (e379bd)
by Pierre-Henry
33:06
created

Smarty_Security::exitTemplate()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 3
nc 2
nop 0
dl 0
loc 6
rs 9.4285
c 0
b 0
f 0
1
<?php
2
/**
3
 * Smarty plugin
4
 *
5
 * @package    Smarty
6
 * @subpackage Security
7
 * @author     Uwe Tews
8
 */
9
10
/*
11
 * FIXME: Smarty_Security API
12
 *      - getter and setter instead of public properties would allow cultivating an internal cache properly
13
 *      - current implementation of isTrustedResourceDir() assumes that Smarty::$template_dir and Smarty::$config_dir are immutable
14
 *        the cache is killed every time either of the variables change. That means that two distinct Smarty objects with differing
15
 *        $template_dir or $config_dir should NOT share the same Smarty_Security instance,
16
 *        as this would lead to (severe) performance penalty! how should this be handled?
17
 */
18
19
/**
20
 * This class does contain the security settings
21
 */
22
class Smarty_Security
23
{
24
    /**
25
     * This determines how Smarty handles "<?php ... ?>" tags in templates.
26
     * possible values:
27
     * <ul>
28
     *   <li>Smarty::PHP_PASSTHRU -> echo PHP tags as they are</li>
29
     *   <li>Smarty::PHP_QUOTE    -> escape tags as entities</li>
30
     *   <li>Smarty::PHP_REMOVE   -> remove php tags</li>
31
     *   <li>Smarty::PHP_ALLOW    -> execute php tags</li>
32
     * </ul>
33
     *
34
     * @var integer
35
     */
36
    public $php_handling = Smarty::PHP_PASSTHRU;
37
38
    /**
39
     * This is the list of template directories that are considered secure.
40
     * $template_dir is in this list implicitly.
41
     *
42
     * @var array
43
     */
44
    public $secure_dir = array();
45
46
    /**
47
     * This is an array of directories where trusted php scripts reside.
48
     * {@link $security} is disabled during their inclusion/execution.
49
     *
50
     * @var array
51
     */
52
    public $trusted_dir = array();
53
54
    /**
55
     * List of regular expressions (PCRE) that include trusted URIs
56
     *
57
     * @var array
58
     */
59
    public $trusted_uri = array();
60
61
    /**
62
     * List of trusted constants names
63
     *
64
     * @var array
65
     */
66
    public $trusted_constants = array();
67
68
    /**
69
     * This is an array of trusted static classes.
70
     * If empty access to all static classes is allowed.
71
     * If set to 'none' none is allowed.
72
     *
73
     * @var array
74
     */
75
    public $static_classes = array();
76
77
    /**
78
     * This is an nested array of trusted classes and static methods.
79
     * If empty access to all static classes and methods is allowed.
80
     * Format:
81
     * array (
82
     *         'class_1' => array('method_1', 'method_2'), // allowed methods listed
83
     *         'class_2' => array(),                       // all methods of class allowed
84
     *       )
85
     * If set to null none is allowed.
86
     *
87
     * @var array
88
     */
89
    public $trusted_static_methods = array();
90
91
    /**
92
     * This is an array of trusted static properties.
93
     * If empty access to all static classes and properties is allowed.
94
     * Format:
95
     * array (
96
     *         'class_1' => array('prop_1', 'prop_2'), // allowed properties listed
97
     *         'class_2' => array(),                   // all properties of class allowed
98
     *       )
99
     * If set to null none is allowed.
100
     *
101
     * @var array
102
     */
103
    public $trusted_static_properties = array();
104
105
    /**
106
     * This is an array of trusted PHP functions.
107
     * If empty all functions are allowed.
108
     * To disable all PHP functions set $php_functions = null.
109
     *
110
     * @var array
111
     */
112
    public $php_functions = array('isset', 'empty', 'count', 'sizeof', 'in_array', 'is_array', 'time',);
113
114
    /**
115
     * This is an array of trusted PHP modifiers.
116
     * If empty all modifiers are allowed.
117
     * To disable all modifier set $php_modifiers = null.
118
     *
119
     * @var array
120
     */
121
    public $php_modifiers = array('escape', 'count', 'nl2br',);
122
123
    /**
124
     * This is an array of allowed tags.
125
     * If empty no restriction by allowed_tags.
126
     *
127
     * @var array
128
     */
129
    public $allowed_tags = array();
130
131
    /**
132
     * This is an array of disabled tags.
133
     * If empty no restriction by disabled_tags.
134
     *
135
     * @var array
136
     */
137
    public $disabled_tags = array();
138
139
    /**
140
     * This is an array of allowed modifier plugins.
141
     * If empty no restriction by allowed_modifiers.
142
     *
143
     * @var array
144
     */
145
    public $allowed_modifiers = array();
146
147
    /**
148
     * This is an array of disabled modifier plugins.
149
     * If empty no restriction by disabled_modifiers.
150
     *
151
     * @var array
152
     */
153
    public $disabled_modifiers = array();
154
155
    /**
156
     * This is an array of disabled special $smarty variables.
157
     *
158
     * @var array
159
     */
160
    public $disabled_special_smarty_vars = array();
161
162
    /**
163
     * This is an array of trusted streams.
164
     * If empty all streams are allowed.
165
     * To disable all streams set $streams = null.
166
     *
167
     * @var array
168
     */
169
    public $streams = array('file');
170
171
    /**
172
     * + flag if constants can be accessed from template
173
     *
174
     * @var boolean
175
     */
176
    public $allow_constants = true;
177
178
    /**
179
     * + flag if super globals can be accessed from template
180
     *
181
     * @var boolean
182
     */
183
    public $allow_super_globals = true;
184
185
    /**
186
     * max template nesting level
187
     *
188
     * @var int
189
     */
190
    public $max_template_nesting = 0;
191
192
    /**
193
     * current template nesting level
194
     *
195
     * @var int
196
     */
197
    private $_current_template_nesting = 0;
198
199
    /**
200
     * Cache for $resource_dir lookup
201
     *
202
     * @var array
203
     */
204
    protected $_resource_dir = array();
205
206
    /**
207
     * Cache for $template_dir lookup
208
     *
209
     * @var array
210
     */
211
    protected $_template_dir = array();
212
213
    /**
214
     * Cache for $config_dir lookup
215
     *
216
     * @var array
217
     */
218
    protected $_config_dir = array();
219
220
    /**
221
     * Cache for $secure_dir lookup
222
     *
223
     * @var array
224
     */
225
    protected $_secure_dir = array();
226
227
    /**
228
     * Cache for $php_resource_dir lookup
229
     *
230
     * @var array
231
     */
232
    protected $_php_resource_dir = null;
233
234
    /**
235
     * Cache for $trusted_dir lookup
236
     *
237
     * @var array
238
     */
239
    protected $_trusted_dir = null;
240
241
    /**
242
     * Cache for include path status
243
     *
244
     * @var bool
245
     */
246
    protected $_include_path_status = false;
247
248
    /**
249
     * Cache for $_include_array lookup
250
     *
251
     * @var array
252
     */
253
    protected $_include_dir = array();
254
255
    /**
256
     * @param Smarty $smarty
257
     */
258
    public function __construct($smarty)
259
    {
260
        $this->smarty = $smarty;
0 ignored issues
show
Bug introduced by
The property smarty does not seem to exist. Did you mean disabled_special_smarty_vars?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
261
        $this->smarty->_cache[ 'template_dir_new' ] = true;
0 ignored issues
show
Bug introduced by
The property smarty does not seem to exist. Did you mean disabled_special_smarty_vars?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
262
        $this->smarty->_cache[ 'config_dir_new' ] = true;
0 ignored issues
show
Bug introduced by
The property smarty does not seem to exist. Did you mean disabled_special_smarty_vars?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
263
    }
264
265
    /**
266
     * Check if PHP function is trusted.
267
     *
268
     * @param  string $function_name
269
     * @param  object $compiler compiler object
270
     *
271
     * @return boolean                 true if function is trusted
272
     * @throws SmartyCompilerException if php function is not trusted
273
     */
274
    public function isTrustedPhpFunction($function_name, $compiler)
275
    {
276
        if (isset($this->php_functions) &&
277
            (empty($this->php_functions) || in_array($function_name, $this->php_functions))
278
        ) {
279
            return true;
280
        }
281
282
        $compiler->trigger_template_error("PHP function '{$function_name}' not allowed by security setting");
283
284
        return false; // should not, but who knows what happens to the compiler in the future?
285
    }
286
287
    /**
288
     * Check if static class is trusted.
289
     *
290
     * @param  string $class_name
291
     * @param  object $compiler compiler object
292
     *
293
     * @return boolean                 true if class is trusted
294
     * @throws SmartyCompilerException if static class is not trusted
295
     */
296
    public function isTrustedStaticClass($class_name, $compiler)
297
    {
298
        if (isset($this->static_classes) &&
299
            (empty($this->static_classes) || in_array($class_name, $this->static_classes))
300
        ) {
301
            return true;
302
        }
303
304
        $compiler->trigger_template_error("access to static class '{$class_name}' not allowed by security setting");
305
306
        return false; // should not, but who knows what happens to the compiler in the future?
307
    }
308
309
    /**
310
     * Check if static class method/property is trusted.
311
     *
312
     * @param  string $class_name
313
     * @param  string $params
314
     * @param  object $compiler compiler object
315
     *
316
     * @return boolean                 true if class method is trusted
317
     * @throws SmartyCompilerException if static class method is not trusted
318
     */
319
    public function isTrustedStaticClassAccess($class_name, $params, $compiler)
320
    {
321
        if (!isset($params[ 2 ])) {
322
            // fall back
323
            return $this->isTrustedStaticClass($class_name, $compiler);
324
        }
325
        if ($params[ 2 ] == 'method') {
326
            $allowed = $this->trusted_static_methods;
327
            $name = substr($params[ 0 ], 0, strpos($params[ 0 ], '('));
328
        } else {
329
            $allowed = $this->trusted_static_properties;
330
            // strip '$'
331
            $name = substr($params[ 0 ], 1);
332
        }
333
        if (isset($allowed)) {
334
            if (empty($allowed)) {
335
                // fall back
336
                return $this->isTrustedStaticClass($class_name, $compiler);
337
            }
338
            if (isset($allowed[ $class_name ]) &&
339
                (empty($allowed[ $class_name ]) || in_array($name, $allowed[ $class_name ]))
340
            ) {
341
                return true;
342
            }
343
        }
344
        $compiler->trigger_template_error("access to static class '{$class_name}' {$params[2]} '{$name}' not allowed by security setting");
345
        return false; // should not, but who knows what happens to the compiler in the future?
346
    }
347
348
    /**
349
     * Check if PHP modifier is trusted.
350
     *
351
     * @param  string $modifier_name
352
     * @param  object $compiler compiler object
353
     *
354
     * @return boolean                 true if modifier is trusted
355
     * @throws SmartyCompilerException if modifier is not trusted
356
     */
357
    public function isTrustedPhpModifier($modifier_name, $compiler)
358
    {
359
        if (isset($this->php_modifiers) &&
360
            (empty($this->php_modifiers) || in_array($modifier_name, $this->php_modifiers))
361
        ) {
362
            return true;
363
        }
364
365
        $compiler->trigger_template_error("modifier '{$modifier_name}' not allowed by security setting");
366
367
        return false; // should not, but who knows what happens to the compiler in the future?
368
    }
369
370
    /**
371
     * Check if tag is trusted.
372
     *
373
     * @param  string $tag_name
374
     * @param  object $compiler compiler object
375
     *
376
     * @return boolean                 true if tag is trusted
377
     * @throws SmartyCompilerException if modifier is not trusted
378
     */
379
    public function isTrustedTag($tag_name, $compiler)
380
    {
381
        // check for internal always required tags
382
        if (in_array($tag_name,
383
                     array('assign', 'call', 'private_filter', 'private_block_plugin', 'private_function_plugin',
384
                           'private_object_block_function', 'private_object_function', 'private_registered_function',
385
                           'private_registered_block', 'private_special_variable', 'private_print_expression',
386
                           'private_modifier'))) {
387
            return true;
388
        }
389
        // check security settings
390
        if (empty($this->allowed_tags)) {
391
            if (empty($this->disabled_tags) || !in_array($tag_name, $this->disabled_tags)) {
392
                return true;
393
            } else {
394
                $compiler->trigger_template_error("tag '{$tag_name}' disabled by security setting", null, true);
395
            }
396
        } elseif (in_array($tag_name, $this->allowed_tags) && !in_array($tag_name, $this->disabled_tags)) {
397
            return true;
398
        } else {
399
            $compiler->trigger_template_error("tag '{$tag_name}' not allowed by security setting", null, true);
400
        }
401
402
        return false; // should not, but who knows what happens to the compiler in the future?
403
    }
404
405
    /**
406
     * Check if special $smarty variable is trusted.
407
     *
408
     * @param  string $var_name
409
     * @param  object $compiler compiler object
410
     *
411
     * @return boolean                 true if tag is trusted
412
     * @throws SmartyCompilerException if modifier is not trusted
413
     */
414
    public function isTrustedSpecialSmartyVar($var_name, $compiler)
415
    {
416
        if (!in_array($var_name, $this->disabled_special_smarty_vars)) {
417
            return true;
418
        } else {
419
            $compiler->trigger_template_error("special variable '\$smarty.{$var_name}' not allowed by security setting",
420
                                              null, true);
421
        }
422
423
        return false; // should not, but who knows what happens to the compiler in the future?
424
    }
425
426
    /**
427
     * Check if modifier plugin is trusted.
428
     *
429
     * @param  string $modifier_name
430
     * @param  object $compiler compiler object
431
     *
432
     * @return boolean                 true if tag is trusted
433
     * @throws SmartyCompilerException if modifier is not trusted
434
     */
435
    public function isTrustedModifier($modifier_name, $compiler)
436
    {
437
        // check for internal always allowed modifier
438
        if (in_array($modifier_name, array('default'))) {
439
            return true;
440
        }
441
        // check security settings
442
        if (empty($this->allowed_modifiers)) {
443
            if (empty($this->disabled_modifiers) || !in_array($modifier_name, $this->disabled_modifiers)) {
444
                return true;
445
            } else {
446
                $compiler->trigger_template_error("modifier '{$modifier_name}' disabled by security setting", null,
447
                                                  true);
448
            }
449
        } elseif (in_array($modifier_name, $this->allowed_modifiers) &&
450
                  !in_array($modifier_name, $this->disabled_modifiers)
451
        ) {
452
            return true;
453
        } else {
454
            $compiler->trigger_template_error("modifier '{$modifier_name}' not allowed by security setting", null,
455
                                              true);
456
        }
457
458
        return false; // should not, but who knows what happens to the compiler in the future?
459
    }
460
461
    /**
462
     * Check if constants are enabled or trusted
463
     *
464
     * @param  string $const    constant name
465
     * @param  object $compiler compiler object
466
     *
467
     * @return bool
468
     */
469
    public function isTrustedConstant($const, $compiler)
470
    {
471
        if (in_array($const, array('true', 'false', 'null'))) {
472
            return true;
473
        }
474
        if (!empty($this->trusted_constants)) {
475
            if (!in_array(strtolower($const), $this->trusted_constants)) {
476
                $compiler->trigger_template_error("Security: access to constant '{$const}' not permitted");
477
                return false;
478
            }
479
            return true;
480
        }
481
        if ($this->allow_constants) {
482
            return true;
483
        }
484
        $compiler->trigger_template_error("Security: access to constants not permitted");
485
        return false;
486
    }
487
488
    /**
489
     * Check if stream is trusted.
490
     *
491
     * @param  string $stream_name
492
     *
493
     * @return boolean         true if stream is trusted
494
     * @throws SmartyException if stream is not trusted
495
     */
496
    public function isTrustedStream($stream_name)
497
    {
498
        if (isset($this->streams) && (empty($this->streams) || in_array($stream_name, $this->streams))) {
499
            return true;
500
        }
501
502
        throw new SmartyException("stream '{$stream_name}' not allowed by security setting");
503
    }
504
505
    /**
506
     * Check if directory of file resource is trusted.
507
     *
508
     * @param  string   $filepath
509
     * @param null|bool $isConfig
510
     *
511
     * @return bool true if directory is trusted
512
     * @throws \SmartyException if directory is not trusted
513
     */
514
    public function isTrustedResourceDir($filepath, $isConfig = null)
515
    {
516
        if ($this->_include_path_status !== $this->smarty->use_include_path) {
0 ignored issues
show
Bug introduced by
The property smarty does not seem to exist. Did you mean disabled_special_smarty_vars?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
517
            foreach ($this->_include_dir as $directory) {
518
                unset($this->_resource_dir[ $directory ]);
519
            }
520
            if ($this->smarty->use_include_path) {
0 ignored issues
show
Bug introduced by
The property smarty does not seem to exist. Did you mean disabled_special_smarty_vars?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
521
                $this->_include_dir = array();
522
                $_dirs = $this->smarty->ext->_getIncludePath->getIncludePathDirs($this->smarty);
0 ignored issues
show
Bug introduced by
The property smarty does not seem to exist. Did you mean disabled_special_smarty_vars?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
523
                foreach ($_dirs as $directory) {
524
                    $this->_include_dir[] = $directory;
525
                    $this->_resource_dir[ $directory ] = true;
526
                }
527
            }
528
            $this->_include_path_status = $this->smarty->use_include_path;
0 ignored issues
show
Bug introduced by
The property smarty does not seem to exist. Did you mean disabled_special_smarty_vars?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
529
        }
530
        if ($isConfig !== true &&
531
            (!isset($this->smarty->_cache[ 'template_dir_new' ]) || $this->smarty->_cache[ 'template_dir_new' ])
0 ignored issues
show
Bug introduced by
The property smarty does not seem to exist. Did you mean disabled_special_smarty_vars?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
532
        ) {
533
            $_dir = $this->smarty->getTemplateDir();
0 ignored issues
show
Bug introduced by
The property smarty does not seem to exist. Did you mean disabled_special_smarty_vars?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
534
            if ($this->_template_dir !== $_dir) {
535
                foreach ($this->_template_dir as $directory) {
536
                    unset($this->_resource_dir[ $directory ]);
537
                }
538
                foreach ($_dir as $directory) {
539
                    $this->_resource_dir[ $directory ] = true;
540
                }
541
                $this->_template_dir = $_dir;
542
            }
543
            $this->smarty->_cache[ 'template_dir_new' ] = false;
0 ignored issues
show
Bug introduced by
The property smarty does not seem to exist. Did you mean disabled_special_smarty_vars?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
544
        }
545
        if ($isConfig !== false &&
546
            (!isset($this->smarty->_cache[ 'config_dir_new' ]) || $this->smarty->_cache[ 'config_dir_new' ])
0 ignored issues
show
Bug introduced by
The property smarty does not seem to exist. Did you mean disabled_special_smarty_vars?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
547
        ) {
548
            $_dir = $this->smarty->getConfigDir();
0 ignored issues
show
Bug introduced by
The property smarty does not seem to exist. Did you mean disabled_special_smarty_vars?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
549
            if ($this->_config_dir !== $_dir) {
550
                foreach ($this->_config_dir as $directory) {
551
                    unset($this->_resource_dir[ $directory ]);
552
                }
553
                foreach ($_dir as $directory) {
554
                    $this->_resource_dir[ $directory ] = true;
555
                }
556
                $this->_config_dir = $_dir;
557
            }
558
            $this->smarty->_cache[ 'config_dir_new' ] = false;
0 ignored issues
show
Bug introduced by
The property smarty does not seem to exist. Did you mean disabled_special_smarty_vars?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
559
        }
560
        if ($this->_secure_dir !== (array) $this->secure_dir) {
561
            foreach ($this->_secure_dir as $directory) {
562
                unset($this->_resource_dir[ $directory ]);
563
            }
564
            foreach ((array) $this->secure_dir as $directory) {
565
                $directory = $this->smarty->_realpath($directory . DIRECTORY_SEPARATOR, true);
0 ignored issues
show
Bug introduced by
The property smarty does not seem to exist. Did you mean disabled_special_smarty_vars?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
566
                $this->_resource_dir[ $directory ] = true;
567
            }
568
            $this->_secure_dir = (array) $this->secure_dir;
569
        }
570
        $this->_resource_dir = $this->_checkDir($filepath, $this->_resource_dir);
571
        return true;
572
    }
573
574
    /**
575
     * Check if URI (e.g. {fetch} or {html_image}) is trusted
576
     * To simplify things, isTrustedUri() resolves all input to "{$PROTOCOL}://{$HOSTNAME}".
577
     * So "http://username:[email protected]:8080/some-path?some=query-string"
578
     * is reduced to "http://hello.world.example.org" prior to applying the patters from {@link $trusted_uri}.
579
     *
580
     * @param  string $uri
581
     *
582
     * @return boolean         true if URI is trusted
583
     * @throws SmartyException if URI is not trusted
584
     * @uses $trusted_uri for list of patterns to match against $uri
585
     */
586
    public function isTrustedUri($uri)
587
    {
588
        $_uri = parse_url($uri);
589
        if (!empty($_uri[ 'scheme' ]) && !empty($_uri[ 'host' ])) {
590
            $_uri = $_uri[ 'scheme' ] . '://' . $_uri[ 'host' ];
591
            foreach ($this->trusted_uri as $pattern) {
592
                if (preg_match($pattern, $_uri)) {
593
                    return true;
594
                }
595
            }
596
        }
597
598
        throw new SmartyException("URI '{$uri}' not allowed by security setting");
599
    }
600
601
    /**
602
     * Check if directory of file resource is trusted.
603
     *
604
     * @param  string $filepath
605
     *
606
     * @return boolean         true if directory is trusted
607
     * @throws SmartyException if PHP directory is not trusted
608
     */
609
    public function isTrustedPHPDir($filepath)
610
    {
611
        if (empty($this->trusted_dir)) {
612
            throw new SmartyException("directory '{$filepath}' not allowed by security setting (no trusted_dir specified)");
613
        }
614
615
        // check if index is outdated
616
        if (!$this->_trusted_dir || $this->_trusted_dir !== $this->trusted_dir) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->_trusted_dir of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
617
            $this->_php_resource_dir = array();
618
619
            $this->_trusted_dir = $this->trusted_dir;
620
            foreach ((array) $this->trusted_dir as $directory) {
621
                $directory = $this->smarty->_realpath($directory . DIRECTORY_SEPARATOR, true);
0 ignored issues
show
Bug introduced by
The property smarty does not seem to exist. Did you mean disabled_special_smarty_vars?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
622
                $this->_php_resource_dir[ $directory ] = true;
623
            }
624
        }
625
626
        $this->_php_resource_dir =
627
            $this->_checkDir($this->smarty->_realpath($filepath, true), $this->_php_resource_dir);
0 ignored issues
show
Bug introduced by
The property smarty does not seem to exist. Did you mean disabled_special_smarty_vars?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
628
        return true;
629
    }
630
631
    /**
632
     * Check if file is inside a valid directory
633
     *
634
     * @param string $filepath
635
     * @param array  $dirs valid directories
636
     *
637
     * @return array
638
     * @throws \SmartyException
639
     */
640
    private function _checkDir($filepath, $dirs)
641
    {
642
        $directory = dirname($filepath) . DIRECTORY_SEPARATOR;
643
        $_directory = array();
644
        while (true) {
645
            // remember the directory to add it to _resource_dir in case we're successful
646
            $_directory[ $directory ] = true;
647
            // test if the directory is trusted
648
            if (isset($dirs[ $directory ])) {
649
                // merge sub directories of current $directory into _resource_dir to speed up subsequent lookup
650
                $dirs = array_merge($dirs, $_directory);
651
652
                return $dirs;
653
            }
654
            // abort if we've reached root
655
            if (!preg_match('#[\\\/][^\\\/]+[\\\/]$#', $directory)) {
656
                break;
657
            }
658
            // bubble up one level
659
            $directory = preg_replace('#[\\\/][^\\\/]+[\\\/]$#', DIRECTORY_SEPARATOR, $directory);
660
        }
661
662
        // give up
663
        throw new SmartyException("directory '{$filepath}' not allowed by security setting");
664
    }
665
666
    /**
667
     * Loads security class and enables security
668
     *
669
     * @param \Smarty                 $smarty
670
     * @param  string|Smarty_Security $security_class if a string is used, it must be class-name
671
     *
672
     * @return \Smarty current Smarty instance for chaining
673
     * @throws \SmartyException when an invalid class name is provided
674
     */
675
    public static function enableSecurity(Smarty $smarty, $security_class)
676
    {
677
        if ($security_class instanceof Smarty_Security) {
678
            $smarty->security_policy = $security_class;
679
            return;
680
        } elseif (is_object($security_class)) {
681
            throw new SmartyException("Class '" . get_class($security_class) . "' must extend Smarty_Security.");
682
        }
683
        if ($security_class == null) {
684
            $security_class = $smarty->security_class;
685
        }
686
        if (!class_exists($security_class)) {
687
            throw new SmartyException("Security class '$security_class' is not defined");
688
        } elseif ($security_class !== 'Smarty_Security' && !is_subclass_of($security_class, 'Smarty_Security')) {
689
            throw new SmartyException("Class '$security_class' must extend Smarty_Security.");
690
        } else {
691
            $smarty->security_policy = new $security_class($smarty);
692
        }
693
        return;
694
    }
695
    /**
696
     * Start template processing
697
     *
698
     * @param $template
699
     *
700
     * @throws SmartyException
701
     */
702
    public function startTemplate($template)
703
    {
704
        if ($this->max_template_nesting > 0 && $this->_current_template_nesting ++ >= $this->max_template_nesting) {
705
            throw new SmartyException("maximum template nesting level of '{$this->max_template_nesting}' exceeded when calling '{$template->template_resource}'");
706
        }
707
    }
708
709
    /**
710
     * Exit template processing
711
     *
712
     */
713
    public function endTemplate()
714
    {
715
        if ($this->max_template_nesting > 0) {
716
            $this->_current_template_nesting --;
717
        }
718
    }
719
720
    /**
721
     * Register callback functions call at start/end of template rendering
722
     *
723
     * @param \Smarty_Internal_Template $template
724
     */
725
    public function registerCallBacks(Smarty_Internal_Template $template)
726
    {
727
        $template->startRenderCallbacks[] = array($this, 'startTemplate');
728
        $template->endRenderCallbacks[] = array($this, 'endTemplate');
729
    }
730
}
731