Security update to Drupal 8.4.6 with PHP held back to 7.0.27 to match the stoneboat...
[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             '1_range_operator' => '{{ (1..2)[0] }}',
40         );
41     }
42
43     /**
44      * @expectedException        Twig_Sandbox_SecurityError
45      * @expectedExceptionMessage Filter "json_encode" is not allowed in "1_child" at line 3.
46      */
47     public function testSandboxWithInheritance()
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
59     public function testSandboxUnallowedMethodAccessor()
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
72     public function testSandboxUnallowedFilter()
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
96     public function testSandboxUnallowedProperty()
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
109     public function testSandboxUnallowedToString()
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
122     public function testSandboxUnallowedToStringArray()
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
135     public function testSandboxUnallowedFunction()
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
147     public function testSandboxUnallowedRangeOperator()
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
159     public function testSandboxAllowMethodFoo()
160     {
161         $twig = $this->getEnvironment(true, array(), self::$templates, array(), array(), array('FooObject' => 'foo'));
162         FooObject::reset();
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
167     public function testSandboxAllowMethodToString()
168     {
169         $twig = $this->getEnvironment(true, array(), self::$templates, array(), array(), array('FooObject' => '__toString'));
170         FooObject::reset();
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
175     public function testSandboxAllowMethodToStringDisabled()
176     {
177         $twig = $this->getEnvironment(false, array(), self::$templates);
178         FooObject::reset();
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
207     public function testSandboxAllowRangeOperator()
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
213     public function testSandboxAllowFunctionsCaseInsensitive()
214     {
215         foreach (array('getfoobar', 'getFoobar', 'getFooBar') as $name) {
216             $twig = $this->getEnvironment(true, array(), self::$templates, array(), array(), array('FooObject' => $name));
217             FooObject::reset();
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
225     public function testSandboxLocallySetForAnInclude()
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') }}
258 EOF
259         ), array('macro', 'import'), array('escape'));
260
261         $this->assertEquals('<p>username</p>', $twig->loadTemplate('index')->render(array()));
262     }
263
264     public function testSandboxDisabledAfterIncludeFunctionError()
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     {
283         $loader = new Twig_Loader_Array($templates);
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
292 class FooObject
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 }