Skip to content

Commit 91cd602

Browse files
committed
Sandboxed template rendering
1 parent ce7ed94 commit 91cd602

File tree

6 files changed

+183
-42
lines changed

6 files changed

+183
-42
lines changed

docs/4.x/config/README.md

Lines changed: 63 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ The most common way to customize your Craft project is by editing files in the [
3232
| [Routing](#url-rules) | `routes.php` | Custom HTTP routes.
3333
| [Application Configuration](#application-configuration) | `app.php`, `app.web.php`, `app.console.php` | Overrides for the root application and any of its [Components](https://www.yiiframework.com/doc/guide/2.0/en/concept-components).
3434
| Plugin Settings | `{plugin-handle}.php`, or other custom files | Consult the plugin’s documentation for specifics.
35-
| [Advanced](#advanced) | | Specific library options and/or behaviors that may be exposed in a non-standard way.
35+
| [Advanced](#advanced) | | Specific library options and/or behaviors that may be exposed in a non-standard way, like [Guzzle](#guzzle) or the Twig [sandbox environment](#twig-sandbox).
3636

3737
::: tip
3838
You may find other files in the `config/` folder, like `license.key` or the `project/` folder. Craft (and many plugins) will ask you to place config-adjacent files here, even if they don’t work in a consistent way.
@@ -515,6 +515,68 @@ The options defined here will be passed into new `GuzzleHttp\Client` instances.
515515
To use a proxy for _all_ requests, set an [httpProxy](config4:httpProxy) in general config. This will get merged with the Guzzle configuration, and passed to the front-end for use by JavaScript, in the [control panel](../control-panel.md). Setting a proxy only in Guzzle’s config will not affect Ajax requests!
516516
:::
517517

518+
#### Twig Sandbox <Since ver="4.17.0" feature="Twig sandbox rendering" />
519+
520+
When <config4:enableTwigSandbox> is _on_, some templates (like [system messages](../mail.md#system-messages)) are rendered in a Twig environment governed by a strict security policy.
521+
522+
You can customize this policy via a `twig-sandbox.php` config file, which is merged on top of [Craft’s defaults](repo:craftcms/cms/blob/4.x/src/config/twig-sandbox.php).
523+
524+
```php
525+
<?php return [
526+
'allowedTags' => [
527+
// Additional Twig tags to allow ...
528+
],
529+
'allowedFilters' => [
530+
// Additional Twig filters to allow ...
531+
],
532+
'allowedFunctions' => [
533+
// Additional Twig functions to allow ...
534+
],
535+
'allowedMethods' => [
536+
// Keys are class names;
537+
// Values are arrays of methods to allow;
538+
craft\elements\Asset::class => [
539+
'getUploader',
540+
],
541+
],
542+
'allowedProperties' => [
543+
// Same format as the above.
544+
],
545+
'allowedClasses' => [
546+
// Each item must be a fully-qualified class name.
547+
// Beware: *all* methods and properties are implicitly allowed!
548+
craft\web\twig\variables\CraftVariable::class,
549+
],
550+
];
551+
```
552+
553+
Internally, Craft uses PHP [attributes](https://www.php.net/manual/en/language.attributes.overview.php) to flag classes, methods, and properties as [allowed](craft4:craft\web\twig\AllowedInSandbox), so they do not appear in the default config array.
554+
555+
::: tip
556+
If you find that sandboxed rendering is too restrictive, try adding features to the security policy one-by-one, _before_ completely disabling it.
557+
:::
558+
559+
560+
#### HTML Purifier
561+
562+
JSON files containing valid [HTML Purifier configuration](https://htmlpurifier.org/live/configdoc/plain.html) can be added to `config/htmlpurifier/`.
563+
564+
When creating a [Redactor](https://plugins.craftcms.com/redactor/) or [CKEditor](https://plugins.craftcms.com/ckeditor/) field, you can select one of your predefined purifier configs—or provide a one-off config object. The [`purify`](../dev/filters.md#purify) filter also accepts a reference to an existing config file or a complete config object.
565+
566+
A simple config that scrubs everything but paragraph and anchor tags would look like this:
567+
568+
```json
569+
{
570+
"HTML.AllowedElements": "p, a",
571+
}
572+
```
573+
574+
For security, any keys _not_ set will use their [defaults](https://github.com/ezyang/htmlpurifier/blob/master/plugins/phorum/config.default.php).
575+
576+
::: tip
577+
Note that HTML Purifier expresses many options with dot notation, like `HTML.AllowedElements`. These are the literal keys, not an indication that keys should be nested!
578+
:::
579+
518580
### Custom Settings
519581

520582
Settings defined in a `config/custom.php` file don’t map to or affect any built-in Craft features, but can useful to centralize data, flags, or secrets that otherwise don’t have a place to live.
@@ -555,26 +617,6 @@ $client->post('/donations', [
555617
If these settings need to be changed frequently, edited by a control panel user, or don’t depend on the environment, they may be a better fit for a [Global Set](../globals.md).
556618
:::
557619

558-
#### HTML Purifier
559-
560-
JSON files containing valid [HTML Purifier configuration](https://htmlpurifier.org/live/configdoc/plain.html) can be added to `config/htmlpurifier/`.
561-
562-
When creating a [Redactor](https://plugins.craftcms.com/redactor/) or [CKEditor](https://plugins.craftcms.com/ckeditor/) field, you can select one of your predefined purifier configs—or provide a one-off config object. The [`purify`](../dev/filters.md#purify) filter also accepts a reference to an existing config file or a complete config object.
563-
564-
A simple config that scrubs everything but paragraph and anchor tags would look like this:
565-
566-
```json
567-
{
568-
"HTML.AllowedElements": "p, a",
569-
}
570-
```
571-
572-
For security, any keys _not_ set will use their [defaults](https://github.com/ezyang/htmlpurifier/blob/master/plugins/phorum/config.default.php).
573-
574-
::: tip
575-
Note that HTML Purifier expresses many options with dot notation, like `HTML.AllowedElements`. These are the literal keys, not an indication that keys should be nested!
576-
:::
577-
578620
<a id="php-constants"></a>
579621

580622
## Bootstrap Config

docs/4.x/extend/controllers.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,32 @@ public function actionFoo(): Response
255255

256256
<craft4:craft\web\Controller::renderTemplate()> calls <craft4:craft\web\View::renderPageTemplate()> internally, which ensures all registered assets are added to the rendered HTML—then it will set the `Content-Type` header on the response, based template’s extension, or `text/html` when a MIME type can’t be determined.
257257

258+
#### Sandboxing <Since ver="4.17.0" feature="Sandboxed Twig template rendering" />
259+
260+
Some plugins may need to render templates defined by a control panel user, as Craft does with [system messages](../mail.md#system-messages).
261+
262+
Providing access to low-level Craft APIs in this way is effectively a form of [remote code execution](https://www.cloudflare.com/learning/security/what-is-remote-code-execution/) for trusted users; whenever possible, this should be restricted to administrators, or enabled via a specific [permission](user-permissions.md).
263+
264+
Your implementation should _always_ process these templates via the special sandbox view methods:
265+
266+
```php
267+
// Load template from a database record or project config:
268+
$notification = Automator::getInstance()->getNotifications()->getActivityNotification();
269+
270+
// Render safely via the sandbox environment:
271+
$message = Craft::$app->getView()->renderSandboxedString($notification->template, [
272+
'user' => $user,
273+
]);
274+
```
275+
276+
To render a regular, on-disk template, use <craft4:craft\web\View::renderSandboxedTemplate()>; to render an [object template](../system/object-templates.md), use <craft4:craft\web\View::renderSandboxedObjectTemplate()>.
277+
278+
If a developer has opted out of sandbox rendering for a project where your plugin is installed (using the <config4:enableTwigSandbox> setting), these methods silently fall back to normal rendering.
279+
280+
::: warning
281+
Any user-space templates that your plugin provides by default (say, seeded during the [install migration](migrations.md#plugin-install-migrations)) should be fully compatible with sandboxed rendering.
282+
:::
283+
258284
#### Registering Assets
259285

260286
To register an asset for inclusion in a rendered page, call one of the <craft4:craft\web\View> methods:

docs/4.x/mail.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,9 @@ Thanks for creating an account with {{systemName}}! To activate your account, cl
4343
If you were not expecting this email, just ignore it.
4444
```
4545

46+
When the <config4:enableTwigSandbox> setting is enabled, Craft uses a special Twig “sandbox” environment to load and render system messages.
47+
Features and APIs available in this mode can be [configured via `twig-sandbox.php`](../configure.md#twig-sandbox). <Since ver="4.17.0" feature="Twig sandbox rendering of system messages" />
48+
4649
#### Variables
4750

4851
Each system message (and subject line) is provided some special, context-specific variables, in addition to those available in normal Twig environments.

docs/5.x/configure.md

Lines changed: 62 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ The most common way to customize your Craft project is by editing files in the [
3636
| [Redirection](system/routing.md#redirection) | `redirects.php` | Additional patterns for redirection.
3737
| [Application Configuration](#application-configuration) | `app.php`, `app.web.php`, `app.console.php` | Overrides for the root application and any of its [Components](https://www.yiiframework.com/doc/guide/2.0/en/concept-components).
3838
| Plugin Settings | `{plugin-handle}.php`, or other custom files | Consult the plugin’s documentation for specifics.
39-
| [Advanced](#advanced) | | Specific library options and/or behaviors that may be exposed in a non-standard way.
39+
| [Advanced](#advanced) | | Specific library options and/or behaviors that may be exposed in a non-standard way, like [Guzzle](#guzzle) or the Twig [sandbox environment](#twig-sandbox).
4040

4141
::: tip
4242
You may find other files in the `config/` folder, like `license.key` or the `project/` folder. Craft (and many plugins) will ask you to place config-adjacent files here, even if they don’t work in a consistent way.
@@ -540,6 +540,67 @@ The options defined here will be passed into new `GuzzleHttp\Client` instances.
540540
To use a proxy for _all_ requests, set an [httpProxy](config5:httpProxy) in general config. This will get merged with the Guzzle configuration, and passed to the front-end for use by JavaScript, in the [control panel](system/control-panel.md). Setting a proxy only in Guzzle’s config will not affect Ajax requests!
541541
:::
542542

543+
#### Twig Sandbox <Since ver="5.9.0" feature="Twig sandbox rendering" />
544+
545+
When <config5:enableTwigSandbox> is _on_, some templates (like [system messages](system/mail.md#system-messages)) are rendered in a Twig environment governed by a strict security policy.
546+
547+
You can customize this policy via a `twig-sandbox.php` config file, which is merged on top of [Craft’s defaults](repo:craftcms/cms/blob/5.x/src/config/twig-sandbox.php).
548+
549+
```php
550+
<?php return [
551+
'allowedTags' => [
552+
// Additional Twig tags to allow ...
553+
],
554+
'allowedFilters' => [
555+
// Additional Twig filters to allow ...
556+
],
557+
'allowedFunctions' => [
558+
// Additional Twig functions to allow ...
559+
],
560+
'allowedMethods' => [
561+
// Keys are class names;
562+
// Values are arrays of methods to allow;
563+
craft\elements\Asset::class => [
564+
'getUploader',
565+
],
566+
],
567+
'allowedProperties' => [
568+
// Same format as the above.
569+
],
570+
'allowedClasses' => [
571+
// Each item must be a fully-qualified class name.
572+
// Beware: *all* methods and properties are implicitly allowed!
573+
craft\web\twig\variables\CraftVariable::class,
574+
],
575+
];
576+
```
577+
578+
Internally, Craft uses PHP [attributes](https://www.php.net/manual/en/language.attributes.overview.php) to flag classes, methods, and properties as [allowed](craft5:craft\web\twig\AllowedInSandbox), so they do not appear in the default config array.
579+
580+
::: tip
581+
If you find that sandboxed rendering is too restrictive, try adding features to the security policy one-by-one, _before_ completely disabling it.
582+
:::
583+
584+
#### HTML Purifier
585+
586+
JSON files containing valid [HTML Purifier configuration](https://htmlpurifier.org/live/configdoc/plain.html) can be added to `config/htmlpurifier/`.
587+
588+
When creating a [Redactor](plugin:redactor) or [CKEditor](plugin:ckeditor) field, you can select one of your predefined purifier configs—or provide a one-off config object. The [`purify`](reference/twig/filters.md#purify) filter also accepts a reference to an existing config file or a complete config object.
589+
590+
A simple config that scrubs everything but paragraph and anchor tags would look like this:
591+
592+
```json
593+
{
594+
"HTML.AllowedElements": "p, a",
595+
}
596+
```
597+
598+
For security, any keys _not_ set will use their [defaults](https://github.com/ezyang/htmlpurifier/blob/master/plugins/phorum/config.default.php).
599+
600+
::: tip
601+
Note that HTML Purifier expresses many options with dot notation, like `HTML.AllowedElements`. These are the literal keys, not an indication that keys should be nested!
602+
:::
603+
543604
### Custom Settings
544605

545606
Settings defined in a `config/custom.php` file don’t map to or affect any built-in Craft features, but can useful to centralize data, flags, or secrets that otherwise don’t have a place to live.
@@ -580,26 +641,6 @@ $client->post('/donations', [
580641
If these settings need to be changed frequently, edited by a control panel user, or don’t depend on the environment, they may be a better fit for a [Global Set](reference/element-types/globals.md).
581642
:::
582643

583-
#### HTML Purifier
584-
585-
JSON files containing valid [HTML Purifier configuration](https://htmlpurifier.org/live/configdoc/plain.html) can be added to `config/htmlpurifier/`.
586-
587-
When creating a [Redactor](plugin:redactor) or [CKEditor](plugin:ckeditor) field, you can select one of your predefined purifier configs—or provide a one-off config object. The [`purify`](reference/twig/filters.md#purify) filter also accepts a reference to an existing config file or a complete config object.
588-
589-
A simple config that scrubs everything but paragraph and anchor tags would look like this:
590-
591-
```json
592-
{
593-
"HTML.AllowedElements": "p, a",
594-
}
595-
```
596-
597-
For security, any keys _not_ set will use their [defaults](https://github.com/ezyang/htmlpurifier/blob/master/plugins/phorum/config.default.php).
598-
599-
::: tip
600-
Note that HTML Purifier expresses many options with dot notation, like `HTML.AllowedElements`. These are the literal keys, not an indication that keys should be nested!
601-
:::
602-
603644
<a id="php-constants"></a>
604645

605646
## Bootstrap Config

docs/5.x/extend/controllers.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,32 @@ public function actionFoo(): Response
259259
Plugins automatically get a [template root](template-roots.md) that exposes templates in their `templates/` directory, but modules must explicitly register a template root.
260260
:::
261261

262+
#### Sandboxing <Since ver="5.9.0" feature="Sandboxed Twig template rendering" />
263+
264+
Some plugins may need to render templates defined by a control panel user, as Craft does with [system messages](../system/mail.md#system-address)).
265+
266+
Providing access to low-level Craft APIs in this way is effectively a form of [remote code execution](https://www.cloudflare.com/learning/security/what-is-remote-code-execution/) for trusted users; whenever possible, this should be restricted to administrators, or enabled via a specific [permission](user-permissions.md).
267+
268+
Your implementation should _always_ process these templates via the special sandbox view methods:
269+
270+
```php
271+
// Load template from a database record or project config:
272+
$notification = Automator::getInstance()->getNotifications()->getActivityNotification();
273+
274+
// Render safely via the sandbox environment:
275+
$message = Craft::$app->getView()->renderSandboxedString($notification->template, [
276+
'user' => $user,
277+
]);
278+
```
279+
280+
To render a regular, on-disk template, use <craft5:craft\web\View::renderSandboxedTemplate()>; to render an [object template](../system/object-templates.md), use <craft5:craft\web\View::renderSandboxedObjectTemplate()>.
281+
282+
If a developer has opted out of sandbox rendering for a project where your plugin is installed (using the <config5:enableTwigSandbox> setting), these methods silently fall back to normal rendering.
283+
284+
::: warning
285+
Any user-space templates that your plugin provides by default (say, seeded during the [install migration](migrations.md#plugin-install-migrations)) should be fully compatible with sandboxed rendering.
286+
:::
287+
262288
#### Registering Assets
263289

264290
To register an asset for inclusion in a rendered page, call one of the <craft5:craft\web\View> methods:

docs/5.x/system/mail.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,9 @@ Thanks for creating an account with {{systemName}}! To activate your account, cl
4242
If you were not expecting this email, just ignore it.
4343
```
4444

45+
When the <config5:enableTwigSandbox> setting is enabled, Craft uses a special Twig “sandbox” environment to load and render system messages.
46+
Features and APIs available in this mode can be [configured via `twig-sandbox.php`](../configure.md#twig-sandbox). <Since ver="5.9.0" feature="Twig sandbox rendering of system messages" />
47+
4548
#### Variables
4649

4750
Each system message (and subject line) is provided some special, context-specific variables, in addition to those available in normal Twig environments.

0 commit comments

Comments
 (0)