Twig_Tests_Extension_SandboxTest::getEnvironment()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 9
Code Lines 6

Duplication

Lines 0
Ratio 0 %
Metric Value
dl 0
loc 9
rs 9.6666
cc 1
eloc 6
nc 1
nop 8

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

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, $templates;
15
16
    public function setUp()
17
    {
18
        self::$params = array(
19
            'name' => 'Fabien',
20
            'obj'  => new FooObject(),
21
            'arr'  => array('obj' => new FooObject()),
22
        );
23
24
        self::$templates = array(
25
            '1_basic1' => '{{ obj.foo }}',
26
            '1_basic2' => '{{ name|upper }}',
27
            '1_basic3' => '{% if name %}foo{% endif %}',
28
            '1_basic4' => '{{ obj.bar }}',
29
            '1_basic5' => '{{ obj }}',
30
            '1_basic6' => '{{ arr.obj }}',
31
            '1_basic7' => '{{ cycle(["foo","bar"], 1) }}',
32
            '1_basic8' => '{{ obj.getfoobar }}{{ obj.getFooBar }}',
33
            '1_basic9' => '{{ obj.foobar }}{{ obj.fooBar }}',
34
            '1_basic'  => '{% if obj.foo %}{{ obj.foo|upper }}{% endif %}',
35
            '1_layout' => '{% block content %}{% endblock %}',
36
            '1_child'  => "{% extends \"1_layout\" %}\n{% block content %}\n{{ \"a\"|json_encode }}\n{% endblock %}",
37
        );
38
    }
39
40
    /**
41
     * @expectedException        Twig_Sandbox_SecurityError
42
     * @expectedExceptionMessage Filter "json_encode" is not allowed in "1_child" at line 3.
43
     */
44
    public function testSandboxWithInheritance()
45
    {
46
        $twig = $this->getEnvironment(true, array(), self::$templates, array('block'));
47
        $twig->loadTemplate('1_child')->render(array());
48
    }
49
50
    public function testSandboxGloballySet()
51
    {
52
        $twig = $this->getEnvironment(false, array(), self::$templates);
53
        $this->assertEquals('FOO', $twig->loadTemplate('1_basic')->render(self::$params), 'Sandbox does nothing if it is disabled globally');
54
55
        $twig = $this->getEnvironment(true, array(), self::$templates);
56
        try {
57
            $twig->loadTemplate('1_basic1')->render(self::$params);
58
            $this->fail('Sandbox throws a SecurityError exception if an unallowed method is called');
59
        } catch (Twig_Sandbox_SecurityError $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
60
        }
61
62
        $twig = $this->getEnvironment(true, array(), self::$templates);
63
        try {
64
            $twig->loadTemplate('1_basic2')->render(self::$params);
65
            $this->fail('Sandbox throws a SecurityError exception if an unallowed filter is called');
66
        } catch (Twig_Sandbox_SecurityError $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
67
        }
68
69
        $twig = $this->getEnvironment(true, array(), self::$templates);
70
        try {
71
            $twig->loadTemplate('1_basic3')->render(self::$params);
72
            $this->fail('Sandbox throws a SecurityError exception if an unallowed tag is used in the template');
73
        } catch (Twig_Sandbox_SecurityError $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
74
        }
75
76
        $twig = $this->getEnvironment(true, array(), self::$templates);
77
        try {
78
            $twig->loadTemplate('1_basic4')->render(self::$params);
79
            $this->fail('Sandbox throws a SecurityError exception if an unallowed property is called in the template');
80
        } catch (Twig_Sandbox_SecurityError $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
81
        }
82
83
        $twig = $this->getEnvironment(true, array(), self::$templates);
84
        try {
85
            $twig->loadTemplate('1_basic5')->render(self::$params);
86
            $this->fail('Sandbox throws a SecurityError exception if an unallowed method (__toString()) is called in the template');
87
        } catch (Twig_Sandbox_SecurityError $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
88
        }
89
90
        $twig = $this->getEnvironment(true, array(), self::$templates);
91
        try {
92
            $twig->loadTemplate('1_basic6')->render(self::$params);
93
            $this->fail('Sandbox throws a SecurityError exception if an unallowed method (__toString()) is called in the template');
94
        } catch (Twig_Sandbox_SecurityError $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
95
        }
96
97
        $twig = $this->getEnvironment(true, array(), self::$templates);
98
        try {
99
            $twig->loadTemplate('1_basic7')->render(self::$params);
100
            $this->fail('Sandbox throws a SecurityError exception if an unallowed function is called in the template');
101
        } catch (Twig_Sandbox_SecurityError $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
102
        }
103
104
        $twig = $this->getEnvironment(true, array(), self::$templates, array(), array(), array('FooObject' => 'foo'));
105
        FooObject::reset();
106
        $this->assertEquals('foo', $twig->loadTemplate('1_basic1')->render(self::$params), 'Sandbox allow some methods');
107
        $this->assertEquals(1, FooObject::$called['foo'], 'Sandbox only calls method once');
108
109
        $twig = $this->getEnvironment(true, array(), self::$templates, array(), array(), array('FooObject' => '__toString'));
110
        FooObject::reset();
111
        $this->assertEquals('foo', $twig->loadTemplate('1_basic5')->render(self::$params), 'Sandbox allow some methods');
112
        $this->assertEquals(1, FooObject::$called['__toString'], 'Sandbox only calls method once');
113
114
        $twig = $this->getEnvironment(false, array(), self::$templates);
115
        FooObject::reset();
116
        $this->assertEquals('foo', $twig->loadTemplate('1_basic5')->render(self::$params), 'Sandbox allows __toString when sandbox disabled');
117
        $this->assertEquals(1, FooObject::$called['__toString'], 'Sandbox only calls method once');
118
119
        $twig = $this->getEnvironment(true, array(), self::$templates, array(), array('upper'));
120
        $this->assertEquals('FABIEN', $twig->loadTemplate('1_basic2')->render(self::$params), 'Sandbox allow some filters');
121
122
        $twig = $this->getEnvironment(true, array(), self::$templates, array('if'));
123
        $this->assertEquals('foo', $twig->loadTemplate('1_basic3')->render(self::$params), 'Sandbox allow some tags');
124
125
        $twig = $this->getEnvironment(true, array(), self::$templates, array(), array(), array(), array('FooObject' => 'bar'));
126
        $this->assertEquals('bar', $twig->loadTemplate('1_basic4')->render(self::$params), 'Sandbox allow some properties');
127
128
        $twig = $this->getEnvironment(true, array(), self::$templates, array(), array(), array(), array(), array('cycle'));
129
        $this->assertEquals('bar', $twig->loadTemplate('1_basic7')->render(self::$params), 'Sandbox allow some functions');
130
131
        foreach (array('getfoobar', 'getFoobar', 'getFooBar') as $name) {
132
            $twig = $this->getEnvironment(true, array(), self::$templates, array(), array(), array('FooObject' => $name));
133
            FooObject::reset();
134
            $this->assertEquals('foobarfoobar', $twig->loadTemplate('1_basic8')->render(self::$params), 'Sandbox allow methods in a case-insensitive way');
135
            $this->assertEquals(2, FooObject::$called['getFooBar'], 'Sandbox only calls method once');
136
137
            $this->assertEquals('foobarfoobar', $twig->loadTemplate('1_basic9')->render(self::$params), 'Sandbox allow methods via shortcut names (ie. without get/set)');
138
        }
139
    }
140
141
    public function testSandboxLocallySetForAnInclude()
142
    {
143
        self::$templates = array(
144
            '2_basic'    => '{{ obj.foo }}{% include "2_included" %}{{ obj.foo }}',
145
            '2_included' => '{% if obj.foo %}{{ obj.foo|upper }}{% endif %}',
146
        );
147
148
        $twig = $this->getEnvironment(false, array(), self::$templates);
149
        $this->assertEquals('fooFOOfoo', $twig->loadTemplate('2_basic')->render(self::$params), 'Sandbox does nothing if disabled globally and sandboxed not used for the include');
150
151
        self::$templates = array(
152
            '3_basic'    => '{{ obj.foo }}{% sandbox %}{% include "3_included" %}{% endsandbox %}{{ obj.foo }}',
153
            '3_included' => '{% if obj.foo %}{{ obj.foo|upper }}{% endif %}',
154
        );
155
156
        $twig = $this->getEnvironment(true, array(), self::$templates);
157
        try {
158
            $twig->loadTemplate('3_basic')->render(self::$params);
159
            $this->fail('Sandbox throws a SecurityError exception when the included file is sandboxed');
160
        } catch (Twig_Sandbox_SecurityError $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
161
        }
162
    }
163
164
    public function testMacrosInASandbox()
165
    {
166
        $twig = $this->getEnvironment(true, array('autoescape' => true), array('index' => <<<EOF
167
{%- import _self as macros %}
168
169
{%- macro test(text) %}<p>{{ text }}</p>{% endmacro %}
170
171
{{- macros.test('username') }}
172
EOF
173
        ), array('macro', 'import'), array('escape'));
174
175
        $this->assertEquals('<p>username</p>', $twig->loadTemplate('index')->render(array()));
176
    }
177
178
    protected function getEnvironment($sandboxed, $options, $templates, $tags = array(), $filters = array(), $methods = array(), $properties = array(), $functions = array())
179
    {
180
        $loader = new Twig_Loader_Array($templates);
181
        $twig = new Twig_Environment($loader, array_merge(array('debug' => true, 'cache' => false, 'autoescape' => false), $options));
182
        $policy = new Twig_Sandbox_SecurityPolicy($tags, $filters, $methods, $properties, $functions);
183
        $twig->addExtension(new Twig_Extension_Sandbox($policy, $sandboxed));
184
185
        return $twig;
186
    }
187
}
188
189
class FooObject
190
{
191
    public static $called = array('__toString' => 0, 'foo' => 0, 'getFooBar' => 0);
192
193
    public $bar = 'bar';
194
195
    public static function reset()
196
    {
197
        self::$called = array('__toString' => 0, 'foo' => 0, 'getFooBar' => 0);
198
    }
199
200
    public function __toString()
201
    {
202
        ++self::$called['__toString'];
203
204
        return 'foo';
205
    }
206
207
    public function foo()
208
    {
209
        ++self::$called['foo'];
210
211
        return 'foo';
212
    }
213
214
    public function getFooBar()
215
    {
216
        ++self::$called['getFooBar'];
217
218
        return 'foobar';
219
    }
220
}
221