Skip to content

Commit 250bb69

Browse files
committed
Backport fb88e6f to 1.1
* Harden theme objects, prevent certain properties from being passed through to ThemeData object * Improve and properly scope Twig security policy - Block methods that write, delete or modify records and attributes in Database/Eloquent and Halcyon models - Block access to theme datasource - Prevent extensions from being created or directly interacted with (models and properties provided to extended objects should still be OK) Refs: fb88e6f#diff-347d3e6f6f84697f5be048027169529a5ed7e782fcf2dcf62dcdbf560a0a4f77
1 parent bce4b59 commit 250bb69

File tree

2 files changed

+129
-36
lines changed

2 files changed

+129
-36
lines changed

modules/cms/classes/Theme.php

+26-15
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,24 @@
1-
<?php namespace Cms\Classes;
2-
3-
use App;
4-
use Url;
5-
use File;
6-
use Yaml;
7-
use Lang;
8-
use Cache;
9-
use Event;
10-
use Config;
11-
use Exception;
12-
use SystemException;
13-
use DirectoryIterator;
14-
use ApplicationException;
1+
<?php
2+
3+
namespace Cms\Classes;
4+
155
use Cms\Models\ThemeData;
6+
use DirectoryIterator;
7+
use Exception;
8+
use Illuminate\Support\Facades\App;
9+
use Illuminate\Support\Facades\Cache;
10+
use Illuminate\Support\Facades\Lang;
1611
use System\Models\Parameter;
12+
use Winter\Storm\Exception\ApplicationException;
13+
use Winter\Storm\Exception\SystemException;
14+
use Winter\Storm\Halcyon\Datasource\DatasourceInterface;
1715
use Winter\Storm\Halcyon\Datasource\DbDatasource;
1816
use Winter\Storm\Halcyon\Datasource\FileDatasource;
19-
use Winter\Storm\Halcyon\Datasource\DatasourceInterface;
17+
use Winter\Storm\Support\Facades\Config;
18+
use Winter\Storm\Support\Facades\Event;
19+
use Winter\Storm\Support\Facades\File;
20+
use Winter\Storm\Support\Facades\Url;
21+
use Winter\Storm\Support\Facades\Yaml;
2022

2123
/**
2224
* This class represents the CMS theme.
@@ -583,6 +585,11 @@ public function getDatasource()
583585
*/
584586
public function __get($name)
585587
{
588+
if (in_array(strtolower($name), ['id', 'path', 'dirname', 'config', 'formconfig', 'previewimageurl'])) {
589+
$method = 'get'. ucfirst($name);
590+
return $this->$method();
591+
}
592+
586593
if ($this->hasCustomData()) {
587594
return $this->getCustomData()->{$name};
588595
}
@@ -597,6 +604,10 @@ public function __get($name)
597604
*/
598605
public function __isset($key)
599606
{
607+
if (in_array(strtolower($key), ['id', 'path', 'dirname', 'config', 'formconfig', 'previewimageurl'])) {
608+
return true;
609+
}
610+
600611
if ($this->hasCustomData()) {
601612
$theme = $this->getCustomData();
602613
return $theme->offsetExists($key);

modules/system/twig/SecurityPolicy.php

+103-21
Original file line numberDiff line numberDiff line change
@@ -1,48 +1,107 @@
11
<?php namespace System\Twig;
22

3+
use Cms\Classes\Controller;
4+
use Cms\Classes\Theme;
5+
use Illuminate\Database\Eloquent\Model as DbModel;
36
use Twig\Markup;
47
use Twig\Template;
58
use Twig\Sandbox\SecurityPolicyInterface;
69
use Twig\Sandbox\SecurityNotAllowedMethodError;
710
use Twig\Sandbox\SecurityNotAllowedPropertyError;
11+
use Winter\Storm\Halcyon\Datasource\DatasourceInterface;
12+
use Winter\Storm\Halcyon\Model as HalcyonModel;
813

914
/**
1015
* SecurityPolicy globally blocks accessibility of certain methods and properties.
1116
*
1217
* @package winter\wn-system-module
13-
* @author Alexey Bobkov, Samuel Georges, Luke Towers
18+
* @author Alexey Bobkov, Samuel Georges, Luke Towers, Ben Thomson
1419
*/
1520
final class SecurityPolicy implements SecurityPolicyInterface
1621
{
1722
/**
18-
* @var array List of forbidden methods.
23+
* @var array<string, string[]> List of forbidden methods, grouped by applicable instance.
1924
*/
2025
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+
];
3184

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+
],
3792
];
3893

3994
/**
4095
* Constructor
4196
*/
4297
public function __construct()
4398
{
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);
46105
}
47106
}
48107

@@ -69,6 +128,19 @@ public function checkSecurity($tags, $filters, $functions)
69128
*/
70129
public function checkPropertyAllowed($obj, $property)
71130
{
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+
}
72144
}
73145

74146
/**
@@ -85,10 +157,20 @@ public function checkMethodAllowed($obj, $method)
85157
return;
86158
}
87159

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+
) {
90165
$class = get_class($obj);
91166
throw new SecurityNotAllowedMethodError(sprintf('Calling "%s" method on a "%s" object is blocked.', $method, $class), $class, $method);
92167
}
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+
}
93175
}
94176
}

0 commit comments

Comments
 (0)