diff --git a/cms/themes.md b/cms/themes.md index 5ff675b5..57075265 100644 --- a/cms/themes.md +++ b/cms/themes.md @@ -125,6 +125,8 @@ parameter = "value" The code in the PHP section executes every time before the template is rendered. The PHP section is optional for all CMS templates and its contents depend on the template type where it is defined. The PHP code section can contain optional open and close PHP tags to enable syntax highlighting in text editors. The open and close tags should always be specified on a different line to the section separator `==`. +> **IMPORTANT:** A CMS template is code, and Winter treats it as code. Editing a page, layout or partial requires `cms.manage_pages`, `cms.manage_layouts` or `cms.manage_partials`, which also grant direct access to the theme's files and to this PHP section, and which belong to the Developer role. As of v1.2.15 theme templates are not rendered in a Twig sandbox and are not intended to be: their Twig markup reaches the same application as their PHP section does. Grant these permissions only to the people you would trust with the source code. If you need to render a template that someone *without* those permissions wrote, use [a sandboxed Twig environment](../plugin/registration#rendering-user-supplied-templates) instead. + ```twig url = "/blog" layout = "default" diff --git a/plugin/registration.md b/plugin/registration.md index 97f542e3..23225de1 100644 --- a/plugin/registration.md +++ b/plugin/registration.md @@ -336,6 +336,41 @@ The following Twig custom options are available: | `deprecated` | boolean | `false` | if true marks the current filter as being deprecated (usually used with `alternative` to provide an alternative option | | `alternative` | string | `''` | if `deprecated` is true, provides a recommended alternative filter to use instead. | +> **NOTE:** Markup tags registered this way are available to theme templates and to your own views. They are deliberately *not* available inside a [sandboxed Twig environment](#rendering-user-supplied-templates), so registering a markup tag can never widen somebody else's sandbox. + +## Rendering user-supplied templates + +Theme templates, plugin views and module views are code: they are written by a developer and they are not sandboxed. If your plugin renders Twig that one of *its users* wrote — a notification body edited in a form field, a document template stored against a record, anything typed into a `codeeditor` — ask for a sandboxed environment instead of the default one: + +```php +use System\Classes\MarkupManager; + +$twig = MarkupManager::makeSandboxedTwigEnvironment(); + +$html = $twig->createTemplate($userEditedTemplate)->render([ + 'order' => $order, +]); +``` + +The sandbox is an allowlist (`System\Twig\SandboxPolicy`). A sandboxed template may use the tags, filters, functions and tests the policy names, read the values you passed to `render()`, and nothing else. It cannot call a function you registered with `registerMarkupTags`, pull in another template with `extends`, `include`, `import` or `use`, or call a method on an object you passed in beyond `__toString()` and `count()`. On a Winter model it can read the attributes and relations the model exposes, and not the raw `attributes` bag, anything the model lists in `$hidden`, or any of the model's own methods. + +So the contract is a single rule: **pass only the values you are willing to expose.** What you pass is readable; nothing else is reachable. + +Widen the allowlist when your own templates legitimately need more than the baseline: + +```php +$twig = MarkupManager::makeSandboxedTwigEnvironment([ + 'filters' => ['my_plugin_filter'], + 'methods' => [\Acme\Shop\Models\Order::class => ['subtotal']], +]); +``` + +The accepted keys are `tags`, `filters`, `functions`, `tests` and `methods`; any other key throws rather than being silently ignored. Each one adds to the baseline, so widening filters leaves the rest of the policy where it was. + +Two things to know before you widen it. A filter or function you allow is allowed with whatever arguments the template chooses, so do not allow one that takes a callable, a file path or a template name. A method you allow is allowed on every instance of that class that reaches the template, including instances you did not pass in yourself. + +Winter itself renders nothing in a sandbox. Theme templates, module and plugin views, and [mail content](../services/mail#message-content) are all managed by permissions that belong to the Developer role, so they are code and are treated as code. This factory is for the one case core does not have: a template that somebody without any Winter permission wrote. + ## Navigation menus Plugins can extend the backend navigation menus by overriding the `registerNavigation` method of the [Plugin registration class](#registration-file). This section shows you how to add menu items to the backend navigation area. An example of registering a top-level navigation menu item with two sub-menu items: diff --git a/services/mail.md b/services/mail.md index 98dbe2eb..a2dbd97b 100644 --- a/services/mail.md +++ b/services/mail.md @@ -300,6 +300,8 @@ Mail::laterOn('queue-name', 5, 'acme.blog::mail.welcome', $data, function ($mess Mail messages can be sent in Winter using either mail views or mail templates. A mail view is supplied by the application or plugin in the file system in the **/views** directory. Whereas a mail template is managed using the backend interface via *System > Mail templates*. All mail messages support using Twig for markup. +> **IMPORTANT:** Mail content is **not** sandboxed, whether it is a view file or a database row edited in the backend. A mail template is code: it may use every markup tag, filter and function a theme template may, and it reaches the same application. Editing one requires `system.manage_mail_templates`, which belongs to the Developer role, so grant it only to the people you would trust with the source code. If you need to render a mail body that one of *your users* wrote, render that fragment yourself in a [sandboxed environment](../plugin/registration#rendering-user-supplied-templates) and pass the result in as data. + Optionally, mail views can be [registered in the Plugin registration file](#registering-mail-layouts-templates-and-partials) with the `registerMailTemplates` method. This will automatically generate a mail template and allows them to be customized using the backend interface. ### Mail views diff --git a/setup/configuration.md b/setup/configuration.md index 8c334600..faf82613 100644 --- a/setup/configuration.md +++ b/setup/configuration.md @@ -201,16 +201,9 @@ The following features are enabled when debug mode is enabled: 1. [Detailed error pages](../cms/pages#error-page) are displayed. 1. Failed user authentication provides a specific reason. 1. [Combined assets](/docs/v1.2/markup/filters/theme) are not minified by default. -1. [Safe mode](#safe-mode) is disabled by default. > **IMPORTANT**: Always set the `app.debug` setting to `false` for production environments. -### Safe mode - -The safe mode setting is found in the `config/cms.php` configuration file with the `enableSafeMode` parameter. The default value is `null`. - -If safe mode is enabled, editing the PHP code section is disabled in CMS templates for security reasons. If set to `null`, safe mode is on when [debug mode](#debug-mode) is disabled. - ### CSRF protection Winter provides an easy method of protecting your application from [cross-site request forgeries](https://en.wikipedia.org/wiki/Cross-site_request_forgery). When enabled, a random token is created in your user sessions, that is then included when the [opening form tag is used](../services/html#form-tokens) or when an [AJAX request](../ajax/introduction.md) is made. This token is then validated server-side to ensure that the request is made by the correct user.