ILIAS  release_5-3 Revision v5.3.23-19-g915713cf615
SandboxTest.php
Go to the documentation of this file.
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12class Twig_Tests_Extension_SandboxTest extends \PHPUnit\Framework\TestCase
13{
14 protected static $params;
15 protected static $templates;
16
17 protected function setUp()
18 {
19 self::$params = array(
20 'name' => 'Fabien',
21 'obj' => new FooObject(),
22 'arr' => array('obj' => new FooObject()),
23 );
24
25 self::$templates = array(
26 '1_basic1' => '{{ obj.foo }}',
27 '1_basic2' => '{{ name|upper }}',
28 '1_basic3' => '{% if name %}foo{% endif %}',
29 '1_basic4' => '{{ obj.bar }}',
30 '1_basic5' => '{{ obj }}',
31 '1_basic6' => '{{ arr.obj }}',
32 '1_basic7' => '{{ cycle(["foo","bar"], 1) }}',
33 '1_basic8' => '{{ obj.getfoobar }}{{ obj.getFooBar }}',
34 '1_basic9' => '{{ obj.foobar }}{{ obj.fooBar }}',
35 '1_basic' => '{% if obj.foo %}{{ obj.foo|upper }}{% endif %}',
36 '1_layout' => '{% block content %}{% endblock %}',
37 '1_child' => "{% extends \"1_layout\" %}\n{% block content %}\n{{ \"a\"|json_encode }}\n{% endblock %}",
38 '1_include' => '{{ include("1_basic1", sandboxed=true) }}',
39 '1_range_operator' => '{{ (1..2)[0] }}',
40 );
41 }
42
48 {
49 $twig = $this->getEnvironment(true, array(), self::$templates, array('block'));
50 $twig->loadTemplate('1_child')->render(array());
51 }
52
53 public function testSandboxGloballySet()
54 {
55 $twig = $this->getEnvironment(false, array(), self::$templates);
56 $this->assertEquals('FOO', $twig->loadTemplate('1_basic')->render(self::$params), 'Sandbox does nothing if it is disabled globally');
57 }
58
60 {
61 $twig = $this->getEnvironment(true, array(), self::$templates);
62 try {
63 $twig->loadTemplate('1_basic1')->render(self::$params);
64 $this->fail('Sandbox throws a SecurityError exception if an unallowed method is called');
65 } catch (Twig_Sandbox_SecurityError $e) {
66 $this->assertInstanceOf('Twig_Sandbox_SecurityNotAllowedMethodError', $e, 'Exception should be an instance of Twig_Sandbox_SecurityNotAllowedMethodError');
67 $this->assertEquals('FooObject', $e->getClassName(), 'Exception should be raised on the "FooObject" class');
68 $this->assertEquals('foo', $e->getMethodName(), 'Exception should be raised on the "foo" method');
69 }
70 }
71
73 {
74 $twig = $this->getEnvironment(true, array(), self::$templates);
75 try {
76 $twig->loadTemplate('1_basic2')->render(self::$params);
77 $this->fail('Sandbox throws a SecurityError exception if an unallowed filter is called');
78 } catch (Twig_Sandbox_SecurityError $e) {
79 $this->assertInstanceOf('Twig_Sandbox_SecurityNotAllowedFilterError', $e, 'Exception should be an instance of Twig_Sandbox_SecurityNotAllowedFilterError');
80 $this->assertEquals('upper', $e->getFilterName(), 'Exception should be raised on the "upper" filter');
81 }
82 }
83
84 public function testSandboxUnallowedTag()
85 {
86 $twig = $this->getEnvironment(true, array(), self::$templates);
87 try {
88 $twig->loadTemplate('1_basic3')->render(self::$params);
89 $this->fail('Sandbox throws a SecurityError exception if an unallowed tag is used in the template');
90 } catch (Twig_Sandbox_SecurityError $e) {
91 $this->assertInstanceOf('Twig_Sandbox_SecurityNotAllowedTagError', $e, 'Exception should be an instance of Twig_Sandbox_SecurityNotAllowedTagError');
92 $this->assertEquals('if', $e->getTagName(), 'Exception should be raised on the "if" tag');
93 }
94 }
95
97 {
98 $twig = $this->getEnvironment(true, array(), self::$templates);
99 try {
100 $twig->loadTemplate('1_basic4')->render(self::$params);
101 $this->fail('Sandbox throws a SecurityError exception if an unallowed property is called in the template');
102 } catch (Twig_Sandbox_SecurityError $e) {
103 $this->assertInstanceOf('Twig_Sandbox_SecurityNotAllowedPropertyError', $e, 'Exception should be an instance of Twig_Sandbox_SecurityNotAllowedPropertyError');
104 $this->assertEquals('FooObject', $e->getClassName(), 'Exception should be raised on the "FooObject" class');
105 $this->assertEquals('bar', $e->getPropertyName(), 'Exception should be raised on the "bar" property');
106 }
107 }
108
110 {
111 $twig = $this->getEnvironment(true, array(), self::$templates);
112 try {
113 $twig->loadTemplate('1_basic5')->render(self::$params);
114 $this->fail('Sandbox throws a SecurityError exception if an unallowed method (__toString()) is called in the template');
115 } catch (Twig_Sandbox_SecurityError $e) {
116 $this->assertInstanceOf('Twig_Sandbox_SecurityNotAllowedMethodError', $e, 'Exception should be an instance of Twig_Sandbox_SecurityNotAllowedMethodError');
117 $this->assertEquals('FooObject', $e->getClassName(), 'Exception should be raised on the "FooObject" class');
118 $this->assertEquals('__tostring', $e->getMethodName(), 'Exception should be raised on the "__toString" method');
119 }
120 }
121
123 {
124 $twig = $this->getEnvironment(true, array(), self::$templates);
125 try {
126 $twig->loadTemplate('1_basic6')->render(self::$params);
127 $this->fail('Sandbox throws a SecurityError exception if an unallowed method (__toString()) is called in the template');
128 } catch (Twig_Sandbox_SecurityError $e) {
129 $this->assertInstanceOf('Twig_Sandbox_SecurityNotAllowedMethodError', $e, 'Exception should be an instance of Twig_Sandbox_SecurityNotAllowedMethodError');
130 $this->assertEquals('FooObject', $e->getClassName(), 'Exception should be raised on the "FooObject" class');
131 $this->assertEquals('__tostring', $e->getMethodName(), 'Exception should be raised on the "__toString" method');
132 }
133 }
134
136 {
137 $twig = $this->getEnvironment(true, array(), self::$templates);
138 try {
139 $twig->loadTemplate('1_basic7')->render(self::$params);
140 $this->fail('Sandbox throws a SecurityError exception if an unallowed function is called in the template');
141 } catch (Twig_Sandbox_SecurityError $e) {
142 $this->assertInstanceOf('Twig_Sandbox_SecurityNotAllowedFunctionError', $e, 'Exception should be an instance of Twig_Sandbox_SecurityNotAllowedFunctionError');
143 $this->assertEquals('cycle', $e->getFunctionName(), 'Exception should be raised on the "cycle" function');
144 }
145 }
146
148 {
149 $twig = $this->getEnvironment(true, array(), self::$templates);
150 try {
151 $twig->loadTemplate('1_range_operator')->render(self::$params);
152 $this->fail('Sandbox throws a SecurityError exception if the unallowed range operator is called');
153 } catch (Twig_Sandbox_SecurityError $e) {
154 $this->assertInstanceOf('Twig_Sandbox_SecurityNotAllowedFunctionError', $e, 'Exception should be an instance of Twig_Sandbox_SecurityNotAllowedFunctionError');
155 $this->assertEquals('range', $e->getFunctionName(), 'Exception should be raised on the "range" function');
156 }
157 }
158
160 {
161 $twig = $this->getEnvironment(true, array(), self::$templates, array(), array(), array('FooObject' => 'foo'));
163 $this->assertEquals('foo', $twig->loadTemplate('1_basic1')->render(self::$params), 'Sandbox allow some methods');
164 $this->assertEquals(1, FooObject::$called['foo'], 'Sandbox only calls method once');
165 }
166
168 {
169 $twig = $this->getEnvironment(true, array(), self::$templates, array(), array(), array('FooObject' => '__toString'));
171 $this->assertEquals('foo', $twig->loadTemplate('1_basic5')->render(self::$params), 'Sandbox allow some methods');
172 $this->assertEquals(1, FooObject::$called['__toString'], 'Sandbox only calls method once');
173 }
174
176 {
177 $twig = $this->getEnvironment(false, array(), self::$templates);
179 $this->assertEquals('foo', $twig->loadTemplate('1_basic5')->render(self::$params), 'Sandbox allows __toString when sandbox disabled');
180 $this->assertEquals(1, FooObject::$called['__toString'], 'Sandbox only calls method once');
181 }
182
183 public function testSandboxAllowFilter()
184 {
185 $twig = $this->getEnvironment(true, array(), self::$templates, array(), array('upper'));
186 $this->assertEquals('FABIEN', $twig->loadTemplate('1_basic2')->render(self::$params), 'Sandbox allow some filters');
187 }
188
189 public function testSandboxAllowTag()
190 {
191 $twig = $this->getEnvironment(true, array(), self::$templates, array('if'));
192 $this->assertEquals('foo', $twig->loadTemplate('1_basic3')->render(self::$params), 'Sandbox allow some tags');
193 }
194
195 public function testSandboxAllowProperty()
196 {
197 $twig = $this->getEnvironment(true, array(), self::$templates, array(), array(), array(), array('FooObject' => 'bar'));
198 $this->assertEquals('bar', $twig->loadTemplate('1_basic4')->render(self::$params), 'Sandbox allow some properties');
199 }
200
201 public function testSandboxAllowFunction()
202 {
203 $twig = $this->getEnvironment(true, array(), self::$templates, array(), array(), array(), array(), array('cycle'));
204 $this->assertEquals('bar', $twig->loadTemplate('1_basic7')->render(self::$params), 'Sandbox allow some functions');
205 }
206
208 {
209 $twig = $this->getEnvironment(true, array(), self::$templates, array(), array(), array(), array(), array('range'));
210 $this->assertEquals('1', $twig->loadTemplate('1_range_operator')->render(self::$params), 'Sandbox allow the range operator');
211 }
212
214 {
215 foreach (array('getfoobar', 'getFoobar', 'getFooBar') as $name) {
216 $twig = $this->getEnvironment(true, array(), self::$templates, array(), array(), array('FooObject' => $name));
218 $this->assertEquals('foobarfoobar', $twig->loadTemplate('1_basic8')->render(self::$params), 'Sandbox allow methods in a case-insensitive way');
219 $this->assertEquals(2, FooObject::$called['getFooBar'], 'Sandbox only calls method once');
220
221 $this->assertEquals('foobarfoobar', $twig->loadTemplate('1_basic9')->render(self::$params), 'Sandbox allow methods via shortcut names (ie. without get/set)');
222 }
223 }
224
226 {
227 self::$templates = array(
228 '2_basic' => '{{ obj.foo }}{% include "2_included" %}{{ obj.foo }}',
229 '2_included' => '{% if obj.foo %}{{ obj.foo|upper }}{% endif %}',
230 );
231
232 $twig = $this->getEnvironment(false, array(), self::$templates);
233 $this->assertEquals('fooFOOfoo', $twig->loadTemplate('2_basic')->render(self::$params), 'Sandbox does nothing if disabled globally and sandboxed not used for the include');
234
235 self::$templates = array(
236 '3_basic' => '{{ obj.foo }}{% sandbox %}{% include "3_included" %}{% endsandbox %}{{ obj.foo }}',
237 '3_included' => '{% if obj.foo %}{{ obj.foo|upper }}{% endif %}',
238 );
239
240 $twig = $this->getEnvironment(true, array(), self::$templates);
241 try {
242 $twig->loadTemplate('3_basic')->render(self::$params);
243 $this->fail('Sandbox throws a SecurityError exception when the included file is sandboxed');
244 } catch (Twig_Sandbox_SecurityError $e) {
245 $this->assertInstanceOf('Twig_Sandbox_SecurityNotAllowedTagError', $e, 'Exception should be an instance of Twig_Sandbox_SecurityNotAllowedTagError');
246 $this->assertEquals('sandbox', $e->getTagName());
247 }
248 }
249
250 public function testMacrosInASandbox()
251 {
252 $twig = $this->getEnvironment(true, array('autoescape' => 'html'), array('index' => <<<EOF
253{%- import _self as macros %}
254
255{%- macro test(text) %}<p>{{ text }}</p>{% endmacro %}
256
257{{- macros.test('username') }}
258EOF
259 ), array('macro', 'import'), array('escape'));
260
261 $this->assertEquals('<p>username</p>', $twig->loadTemplate('index')->render(array()));
262 }
263
265 {
266 $twig = $this->getEnvironment(false, array(), self::$templates);
267
268 $e = null;
269 try {
270 $twig->loadTemplate('1_include')->render(self::$params);
271 } catch (Throwable $e) {
272 } catch (Exception $e) {
273 }
274 if (null === $e) {
275 $this->fail('An exception should be thrown for this test to be valid.');
276 }
277
278 $this->assertFalse($twig->getExtension('Twig_Extension_Sandbox')->isSandboxed(), 'Sandboxed include() function call should not leave Sandbox enabled when an error occurs.');
279 }
280
281 protected function getEnvironment($sandboxed, $options, $templates, $tags = array(), $filters = array(), $methods = array(), $properties = array(), $functions = array())
282 {
284 $twig = new Twig_Environment($loader, array_merge(array('debug' => true, 'cache' => false, 'autoescape' => false), $options));
285 $policy = new Twig_Sandbox_SecurityPolicy($tags, $filters, $methods, $properties, $functions);
286 $twig->addExtension(new Twig_Extension_Sandbox($policy, $sandboxed));
287
288 return $twig;
289 }
290}
291
293{
294 public static $called = array('__toString' => 0, 'foo' => 0, 'getFooBar' => 0);
295
296 public $bar = 'bar';
297
298 public static function reset()
299 {
300 self::$called = array('__toString' => 0, 'foo' => 0, 'getFooBar' => 0);
301 }
302
303 public function __toString()
304 {
305 ++self::$called['__toString'];
306
307 return 'foo';
308 }
309
310 public function foo()
311 {
312 ++self::$called['foo'];
313
314 return 'foo';
315 }
316
317 public function getFooBar()
318 {
319 ++self::$called['getFooBar'];
320
321 return 'foobar';
322 }
323}
const EOF
How fgetc() reports an End Of File.
Definition: JSMin_lib.php:92
if(!isset( $_REQUEST[ 'ReturnTo'])) if(!isset($_REQUEST['AuthId'])) $options
Definition: as_login.php:20
An exception for terminatinating execution or to throw for unit testing.
static reset()
static $called
Stores the Twig configuration.
Definition: Environment.php:18
Loads a template from an array.
Definition: Array.php:27
Exception thrown when a security error occurs at runtime.
Represents a security policy which need to be enforced when sandbox mode is enabled.
testSandboxWithInheritance()
@expectedException Twig_Sandbox_SecurityError @expectedExceptionMessage Filter "json_encode" is not a...
Definition: SandboxTest.php:47
getEnvironment($sandboxed, $options, $templates, $tags=array(), $filters=array(), $methods=array(), $properties=array(), $functions=array())
$tags
Definition: croninfo.php:19
if($format !==null) $name
Definition: metadata.php:146
$params
Definition: disable.php:11