9d90e0ea7467bb735647c615210714345f070337
[yaffs-website] / vendor / twig / twig / test / Twig / Tests / Extension / SandboxTest.php
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
12 class 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         );
40     }
41
42     /**
43      * @expectedException        Twig_Sandbox_SecurityError
44      * @expectedExceptionMessage Filter "json_encode" is not allowed in "1_child" at line 3.
45      */
46     public function testSandboxWithInheritance()
47     {
48         $twig = $this->getEnvironment(true, array(), self::$templates, array('block'));
49         $twig->loadTemplate('1_child')->render(array());
50     }
51
52     public function testSandboxGloballySet()
53     {
54         $twig = $this->getEnvironment(false, array(), self::$templates);
55         $this->assertEquals('FOO', $twig->loadTemplate('1_basic')->render(self::$params), 'Sandbox does nothing if it is disabled globally');
56     }
57
58     public function testSandboxUnallowedMethodAccessor()
59     {
60         $twig = $this->getEnvironment(true, array(), self::$templates);
61         try {
62             $twig->loadTemplate('1_basic1')->render(self::$params);
63             $this->fail('Sandbox throws a SecurityError exception if an unallowed method is called');
64         } catch (Twig_Sandbox_SecurityError $e) {
65             $this->assertInstanceOf('Twig_Sandbox_SecurityNotAllowedMethodError', $e, 'Exception should be an instance of Twig_Sandbox_SecurityNotAllowedMethodError');
66             $this->assertEquals('FooObject', $e->getClassName(), 'Exception should be raised on the "FooObject" class');
67             $this->assertEquals('foo', $e->getMethodName(), 'Exception should be raised on the "foo" method');
68         }
69     }
70
71     public function testSandboxUnallowedFilter()
72     {
73         $twig = $this->getEnvironment(true, array(), self::$templates);
74         try {
75             $twig->loadTemplate('1_basic2')->render(self::$params);
76             $this->fail('Sandbox throws a SecurityError exception if an unallowed filter is called');
77         } catch (Twig_Sandbox_SecurityError $e) {
78             $this->assertInstanceOf('Twig_Sandbox_SecurityNotAllowedFilterError', $e, 'Exception should be an instance of Twig_Sandbox_SecurityNotAllowedFilterError');
79             $this->assertEquals('upper', $e->getFilterName(), 'Exception should be raised on the "upper" filter');
80         }
81     }
82
83     public function testSandboxUnallowedTag()
84     {
85         $twig = $this->getEnvironment(true, array(), self::$templates);
86         try {
87             $twig->loadTemplate('1_basic3')->render(self::$params);
88             $this->fail('Sandbox throws a SecurityError exception if an unallowed tag is used in the template');
89         } catch (Twig_Sandbox_SecurityError $e) {
90             $this->assertInstanceOf('Twig_Sandbox_SecurityNotAllowedTagError', $e, 'Exception should be an instance of Twig_Sandbox_SecurityNotAllowedTagError');
91             $this->assertEquals('if', $e->getTagName(), 'Exception should be raised on the "if" tag');
92         }
93     }
94
95     public function testSandboxUnallowedProperty()
96     {
97         $twig = $this->getEnvironment(true, array(), self::$templates);
98         try {
99             $twig->loadTemplate('1_basic4')->render(self::$params);
100             $this->fail('Sandbox throws a SecurityError exception if an unallowed property is called in the template');
101         } catch (Twig_Sandbox_SecurityError $e) {
102             $this->assertInstanceOf('Twig_Sandbox_SecurityNotAllowedPropertyError', $e, 'Exception should be an instance of Twig_Sandbox_SecurityNotAllowedPropertyError');
103             $this->assertEquals('FooObject', $e->getClassName(), 'Exception should be raised on the "FooObject" class');
104             $this->assertEquals('bar', $e->getPropertyName(), 'Exception should be raised on the "bar" property');
105         }
106     }
107
108     public function testSandboxUnallowedToString()
109     {
110         $twig = $this->getEnvironment(true, array(), self::$templates);
111         try {
112             $twig->loadTemplate('1_basic5')->render(self::$params);
113             $this->fail('Sandbox throws a SecurityError exception if an unallowed method (__toString()) is called in the template');
114         } catch (Twig_Sandbox_SecurityError $e) {
115             $this->assertInstanceOf('Twig_Sandbox_SecurityNotAllowedMethodError', $e, 'Exception should be an instance of Twig_Sandbox_SecurityNotAllowedMethodError');
116             $this->assertEquals('FooObject', $e->getClassName(), 'Exception should be raised on the "FooObject" class');
117             $this->assertEquals('__tostring', $e->getMethodName(), 'Exception should be raised on the "__toString" method');
118         }
119     }
120
121     public function testSandboxUnallowedToStringArray()
122     {
123         $twig = $this->getEnvironment(true, array(), self::$templates);
124         try {
125             $twig->loadTemplate('1_basic6')->render(self::$params);
126             $this->fail('Sandbox throws a SecurityError exception if an unallowed method (__toString()) is called in the template');
127         } catch (Twig_Sandbox_SecurityError $e) {
128             $this->assertInstanceOf('Twig_Sandbox_SecurityNotAllowedMethodError', $e, 'Exception should be an instance of Twig_Sandbox_SecurityNotAllowedMethodError');
129             $this->assertEquals('FooObject', $e->getClassName(), 'Exception should be raised on the "FooObject" class');
130             $this->assertEquals('__tostring', $e->getMethodName(), 'Exception should be raised on the "__toString" method');
131         }
132     }
133
134     public function testSandboxUnallowedFunction()
135     {
136         $twig = $this->getEnvironment(true, array(), self::$templates);
137         try {
138             $twig->loadTemplate('1_basic7')->render(self::$params);
139             $this->fail('Sandbox throws a SecurityError exception if an unallowed function is called in the template');
140         } catch (Twig_Sandbox_SecurityError $e) {
141             $this->assertInstanceOf('Twig_Sandbox_SecurityNotAllowedFunctionError', $e, 'Exception should be an instance of Twig_Sandbox_SecurityNotAllowedFunctionError');
142             $this->assertEquals('cycle', $e->getFunctionName(), 'Exception should be raised on the "cycle" function');
143         }
144     }
145
146     public function testSandboxAllowMethodFoo()
147     {
148         $twig = $this->getEnvironment(true, array(), self::$templates, array(), array(), array('FooObject' => 'foo'));
149         FooObject::reset();
150         $this->assertEquals('foo', $twig->loadTemplate('1_basic1')->render(self::$params), 'Sandbox allow some methods');
151         $this->assertEquals(1, FooObject::$called['foo'], 'Sandbox only calls method once');
152     }
153
154     public function testSandboxAllowMethodToString()
155     {
156         $twig = $this->getEnvironment(true, array(), self::$templates, array(), array(), array('FooObject' => '__toString'));
157         FooObject::reset();
158         $this->assertEquals('foo', $twig->loadTemplate('1_basic5')->render(self::$params), 'Sandbox allow some methods');
159         $this->assertEquals(1, FooObject::$called['__toString'], 'Sandbox only calls method once');
160     }
161
162     public function testSandboxAllowMethodToStringDisabled()
163     {
164         $twig = $this->getEnvironment(false, array(), self::$templates);
165         FooObject::reset();
166         $this->assertEquals('foo', $twig->loadTemplate('1_basic5')->render(self::$params), 'Sandbox allows __toString when sandbox disabled');
167         $this->assertEquals(1, FooObject::$called['__toString'], 'Sandbox only calls method once');
168     }
169
170     public function testSandboxAllowFilter()
171     {
172         $twig = $this->getEnvironment(true, array(), self::$templates, array(), array('upper'));
173         $this->assertEquals('FABIEN', $twig->loadTemplate('1_basic2')->render(self::$params), 'Sandbox allow some filters');
174     }
175
176     public function testSandboxAllowTag()
177     {
178         $twig = $this->getEnvironment(true, array(), self::$templates, array('if'));
179         $this->assertEquals('foo', $twig->loadTemplate('1_basic3')->render(self::$params), 'Sandbox allow some tags');
180     }
181
182     public function testSandboxAllowProperty()
183     {
184         $twig = $this->getEnvironment(true, array(), self::$templates, array(), array(), array(), array('FooObject' => 'bar'));
185         $this->assertEquals('bar', $twig->loadTemplate('1_basic4')->render(self::$params), 'Sandbox allow some properties');
186     }
187
188     public function testSandboxAllowFunction()
189     {
190         $twig = $this->getEnvironment(true, array(), self::$templates, array(), array(), array(), array(), array('cycle'));
191         $this->assertEquals('bar', $twig->loadTemplate('1_basic7')->render(self::$params), 'Sandbox allow some functions');
192     }
193
194     public function testSandboxAllowFunctionsCaseInsensitive()
195     {
196         foreach (array('getfoobar', 'getFoobar', 'getFooBar') as $name) {
197             $twig = $this->getEnvironment(true, array(), self::$templates, array(), array(), array('FooObject' => $name));
198             FooObject::reset();
199             $this->assertEquals('foobarfoobar', $twig->loadTemplate('1_basic8')->render(self::$params), 'Sandbox allow methods in a case-insensitive way');
200             $this->assertEquals(2, FooObject::$called['getFooBar'], 'Sandbox only calls method once');
201
202             $this->assertEquals('foobarfoobar', $twig->loadTemplate('1_basic9')->render(self::$params), 'Sandbox allow methods via shortcut names (ie. without get/set)');
203         }
204     }
205
206     public function testSandboxLocallySetForAnInclude()
207     {
208         self::$templates = array(
209             '2_basic' => '{{ obj.foo }}{% include "2_included" %}{{ obj.foo }}',
210             '2_included' => '{% if obj.foo %}{{ obj.foo|upper }}{% endif %}',
211         );
212
213         $twig = $this->getEnvironment(false, array(), self::$templates);
214         $this->assertEquals('fooFOOfoo', $twig->loadTemplate('2_basic')->render(self::$params), 'Sandbox does nothing if disabled globally and sandboxed not used for the include');
215
216         self::$templates = array(
217             '3_basic' => '{{ obj.foo }}{% sandbox %}{% include "3_included" %}{% endsandbox %}{{ obj.foo }}',
218             '3_included' => '{% if obj.foo %}{{ obj.foo|upper }}{% endif %}',
219         );
220
221         $twig = $this->getEnvironment(true, array(), self::$templates);
222         try {
223             $twig->loadTemplate('3_basic')->render(self::$params);
224             $this->fail('Sandbox throws a SecurityError exception when the included file is sandboxed');
225         } catch (Twig_Sandbox_SecurityError $e) {
226             $this->assertInstanceOf('Twig_Sandbox_SecurityNotAllowedTagError', $e, 'Exception should be an instance of Twig_Sandbox_SecurityNotAllowedTagError');
227             $this->assertEquals('sandbox', $e->getTagName());
228         }
229     }
230
231     public function testMacrosInASandbox()
232     {
233         $twig = $this->getEnvironment(true, array('autoescape' => 'html'), array('index' => <<<EOF
234 {%- import _self as macros %}
235
236 {%- macro test(text) %}<p>{{ text }}</p>{% endmacro %}
237
238 {{- macros.test('username') }}
239 EOF
240         ), array('macro', 'import'), array('escape'));
241
242         $this->assertEquals('<p>username</p>', $twig->loadTemplate('index')->render(array()));
243     }
244
245     public function testSandboxDisabledAfterIncludeFunctionError()
246     {
247         $twig = $this->getEnvironment(false, array(), self::$templates);
248
249         $e = null;
250         try {
251             $twig->loadTemplate('1_include')->render(self::$params);
252         } catch (Throwable $e) {
253         } catch (Exception $e) {
254         }
255         if ($e === null) {
256             $this->fail('An exception should be thrown for this test to be valid.');
257         }
258
259         $this->assertFalse($twig->getExtension('Twig_Extension_Sandbox')->isSandboxed(), 'Sandboxed include() function call should not leave Sandbox enabled when an error occurs.');
260     }
261
262     protected function getEnvironment($sandboxed, $options, $templates, $tags = array(), $filters = array(), $methods = array(), $properties = array(), $functions = array())
263     {
264         $loader = new Twig_Loader_Array($templates);
265         $twig = new Twig_Environment($loader, array_merge(array('debug' => true, 'cache' => false, 'autoescape' => false), $options));
266         $policy = new Twig_Sandbox_SecurityPolicy($tags, $filters, $methods, $properties, $functions);
267         $twig->addExtension(new Twig_Extension_Sandbox($policy, $sandboxed));
268
269         return $twig;
270     }
271 }
272
273 class FooObject
274 {
275     public static $called = array('__toString' => 0, 'foo' => 0, 'getFooBar' => 0);
276
277     public $bar = 'bar';
278
279     public static function reset()
280     {
281         self::$called = array('__toString' => 0, 'foo' => 0, 'getFooBar' => 0);
282     }
283
284     public function __toString()
285     {
286         ++self::$called['__toString'];
287
288         return 'foo';
289     }
290
291     public function foo()
292     {
293         ++self::$called['foo'];
294
295         return 'foo';
296     }
297
298     public function getFooBar()
299     {
300         ++self::$called['getFooBar'];
301
302         return 'foobar';
303     }
304 }