1
1
<?php namespace System \Twig ;
2
2
3
+ use Cms \Classes \Controller ;
4
+ use Cms \Classes \Theme ;
5
+ use Illuminate \Database \Eloquent \Model as DbModel ;
3
6
use Twig \Markup ;
4
7
use Twig \Template ;
5
8
use Twig \Sandbox \SecurityPolicyInterface ;
6
9
use Twig \Sandbox \SecurityNotAllowedMethodError ;
7
10
use Twig \Sandbox \SecurityNotAllowedPropertyError ;
11
+ use Winter \Storm \Halcyon \Datasource \DatasourceInterface ;
12
+ use Winter \Storm \Halcyon \Model as HalcyonModel ;
8
13
9
14
/**
10
15
* SecurityPolicy globally blocks accessibility of certain methods and properties.
11
16
*
12
17
* @package winter\wn-system-module
13
- * @author Alexey Bobkov, Samuel Georges, Luke Towers
18
+ * @author Alexey Bobkov, Samuel Georges, Luke Towers, Ben Thomson
14
19
*/
15
20
final class SecurityPolicy implements SecurityPolicyInterface
16
21
{
17
22
/**
18
- * @var array List of forbidden methods.
23
+ * @var array<string, string[]> List of forbidden methods, grouped by applicable instance .
19
24
*/
20
25
protected $ blockedMethods = [
21
- // Prevent accessing Twig itself
22
- 'getTwig ' ,
23
-
24
- // \Winter\Storm\Extension\ExtendableTrait
25
- 'addDynamicMethod ' ,
26
- 'addDynamicProperty ' ,
27
-
28
- // \Winter\Storm\Support\Traits\Emitter
29
- 'bindEvent ' ,
30
- 'bindEventOnce ' ,
26
+ '* ' => [
27
+ // Prevent accessing Twig itself
28
+ 'getTwig ' ,
29
+ // Prevent extensions of any objects
30
+ 'addDynamicMethod ' ,
31
+ 'addDynamicProperty ' ,
32
+ 'extendClassWith ' ,
33
+ 'getClassExtension ' ,
34
+ 'extendableSet ' ,
35
+ // Prevent binding to events
36
+ 'bindEvent ' ,
37
+ 'bindEventOnce ' ,
38
+ ],
39
+ // Prevent some controller methods
40
+ Controller::class => [
41
+ 'runPage ' ,
42
+ 'renderPage ' ,
43
+ 'getLoader ' ,
44
+ ],
45
+ // Prevent model data modification
46
+ DbModel::class => [
47
+ 'fill ' ,
48
+ 'setAttribute ' ,
49
+ 'setRawAttributes ' ,
50
+ 'save ' ,
51
+ 'push ' ,
52
+ 'update ' ,
53
+ 'delete ' ,
54
+ 'forceDelete ' ,
55
+ ],
56
+ HalcyonModel::class => [
57
+ 'fill ' ,
58
+ 'setAttribute ' ,
59
+ 'setRawAttributes ' ,
60
+ 'setSettingsAttribute ' ,
61
+ 'setFileNameAttribute ' ,
62
+ 'save ' ,
63
+ 'push ' ,
64
+ 'update ' ,
65
+ 'delete ' ,
66
+ 'forceDelete ' ,
67
+ ],
68
+ DatasourceInterface::class => [
69
+ 'insert ' ,
70
+ 'update ' ,
71
+ 'delete ' ,
72
+ 'forceDelete ' ,
73
+ 'write ' ,
74
+ 'usingSource ' ,
75
+ 'pushToSource ' ,
76
+ 'removeFromSource ' ,
77
+ ],
78
+ Theme::class => [
79
+ 'setDirName ' ,
80
+ 'registerHalcyonDatasource ' ,
81
+ 'getDatasource '
82
+ ],
83
+ ];
31
84
32
- // Eloquent & Halcyon data modification
33
- 'insert ' ,
34
- 'update ' ,
35
- 'delete ' ,
36
- 'write ' ,
85
+ /**
86
+ * @var array<string, string[]> List of forbidden properties, grouped by applicable instance.
87
+ */
88
+ protected $ blockedProperties = [
89
+ Theme::class => [
90
+ 'datasource ' ,
91
+ ],
37
92
];
38
93
39
94
/**
40
95
* Constructor
41
96
*/
42
97
public function __construct ()
43
98
{
44
- foreach ($ this ->blockedMethods as $ i => $ m ) {
45
- $ this ->blockedMethods [$ i ] = strtolower ($ m );
99
+ foreach ($ this ->blockedMethods as $ type => $ methods ) {
100
+ $ this ->blockedMethods [$ type ] = array_map ('strtolower ' , $ methods );
101
+ }
102
+
103
+ foreach ($ this ->blockedProperties as $ type => $ properties ) {
104
+ $ this ->blockedProperties [$ type ] = array_map ('strtolower ' , $ properties );
46
105
}
47
106
}
48
107
@@ -69,6 +128,19 @@ public function checkSecurity($tags, $filters, $functions)
69
128
*/
70
129
public function checkPropertyAllowed ($ obj , $ property )
71
130
{
131
+ // No need to check Twig internal objects
132
+ if ($ obj instanceof Template || $ obj instanceof Markup) {
133
+ return ;
134
+ }
135
+
136
+ $ property = strtolower ($ property );
137
+
138
+ foreach ($ this ->blockedProperties as $ type => $ properties ) {
139
+ if ($ obj instanceof $ type && in_array ($ property , $ properties )) {
140
+ $ class = get_class ($ obj );
141
+ throw new SecurityNotAllowedPropertyError (sprintf ('Getting "%s" property in a "%s" object is blocked. ' , $ property , $ class ), $ class , $ property );
142
+ }
143
+ }
72
144
}
73
145
74
146
/**
@@ -85,10 +157,20 @@ public function checkMethodAllowed($obj, $method)
85
157
return ;
86
158
}
87
159
88
- $ blockedMethod = strtolower ($ method );
89
- if (in_array ($ blockedMethod , $ this ->blockedMethods )) {
160
+ $ method = strtolower ($ method );
161
+
162
+ if (
163
+ in_array ($ method , $ this ->blockedMethods ['* ' ])
164
+ ) {
90
165
$ class = get_class ($ obj );
91
166
throw new SecurityNotAllowedMethodError (sprintf ('Calling "%s" method on a "%s" object is blocked. ' , $ method , $ class ), $ class , $ method );
92
167
}
168
+
169
+ foreach ($ this ->blockedMethods as $ type => $ methods ) {
170
+ if ($ obj instanceof $ type && in_array ($ method , $ methods )) {
171
+ $ class = get_class ($ obj );
172
+ throw new SecurityNotAllowedMethodError (sprintf ('Calling "%s" method on a "%s" object is blocked. ' , $ method , $ class ), $ class , $ method );
173
+ }
174
+ }
93
175
}
94
176
}
0 commit comments