nystudio107 / craft-twig-sandbox
Allows you to easily create a sandboxed Twig environment where you can control what tags, filters, functions, and object methods/properties are allowed
Fund package maintenance!
khalwat
Installs: 28 005
Dependents: 3
Suggesters: 0
Security: 0
Stars: 3
Watchers: 4
Forks: 0
Open Issues: 0
Requires
- craftcms/cms: ^5.0.0
- twig/twig: ^3.0.0
Requires (Dev)
- craftcms/ecs: dev-main
- craftcms/phpstan: dev-main
- craftcms/rector: dev-main
- markhuot/craft-pest-core: ^2.0.4
This package is auto-updated.
Last update: 2025-06-09 08:19:05 UTC
README
Craft Twig Sandbox
Allows you to easily create a sandboxed Twig environment where you can control what tags, filters, functions, and object methods/properties are allowed
Requirements
Craft Twig Sandbox requires Craft CMS 5.x
Installation
To install Craft Twig Sandbox, follow these steps:
-
Open your terminal and go to your Craft project:
cd /path/to/project
-
Then tell Composer to require the package:
composer require nystudio107/craft-twig-sandbox
About Craft Twig Sandbox
Rather than just creating a new Twig Environment
for the sandbox, Craft Twig Sandbox sub-classes the Craft View
class, which has a few benefits:
- You get all of the Craft provided tags, filters, functions, objects, globals, etc. available to you if you want
- Plugin-provided tags, filters, functions, and objects are available if you want
- You get access to the familiar
.renderObjectTemplate()
,.renderString()
,.renderPageTemplate()
and.renderTemplate()
methods - All of the normal Craft events and scaffolding related to template rendering are present as well
It also implements an ErrorHandler
that sub-classes the Craft ErrorHandler
which is used to handle exceptions that happen when rendering Twig templates. This allows you to optionally display exceptions such as:
Twig\Sandbox\SecurityNotAllowedFunctionError
Function "dump" is not allowed in "__string_template__b0120324b463b0e0d2c2618b7c5ce3ba" at line 1.
Using Craft Twig Sandbox
In its simplest form, you can create a Twig Sandbox like so:
use nystudio107\crafttwigsandbox\web\SandboxView; $sandboxView = new SandboxView();
This will create a new SandboxView
that works just like the Craft web View
class so you can use any of the View
render methods for Twig templates:
$result = $sandboxView->renderString(); $result = $sandboxView->renderObjectTemplate(); $result = $sandboxView->renderPageTemplate(); $result = $sandboxView->renderTemplate();
...and they will be rendered using the default BlacklistSecurityPolicy
so blacklisted Twig tags, filters, functions, and object methods/properties will not be allowed.
If any tags, filters, functions, or object methods/properties are used that are not allowed by the security policy, a SecurityError
exception will be thrown.
N.B.: For performance reasons, you should create a SandboxView
once, and use it throughout your application's lifecycle, rather than re-creating it every time you want to render Twig using it.
Exception handling
Note that in the above example, exceptions will be thrown if the security policy is violated; so you can handle the exception yourself if you like:
use nystudio107\crafttwigsandbox\web\SandboxView; use Twig\Sandbox\SecurityError; $sandboxView = new SandboxView(); try { $result = $sandboxView->renderTemplate(); } catch (\Throwable $e) { // If this is a Twig Runtime exception, use the previous one instead if ($e instanceof SecurityError && ($previousException = $e->getPrevious()) !== null) { $e = $previousException; } // Exception handling here }
Or if you want to use Craft's default web/console exception handling when rendering templates, you can do that like this:
use nystudio107\crafttwigsandbox\web\SandboxView; $sandboxView = new SandboxView(); try { $result = $sandboxView->renderTemplate(); } catch (\Throwable $e) { $sandboxView->sandboxErrorHandler->handleException($e) }
...and the exception with a full stack trace will be displayed in the web browser, or in the console (depending on the type of the current request).
BlacklistSecurityPolicy
The BlacklistSecurityPolicy
is a SecurityPolicy
that specifies the Twig tags, filters, functions, and object methods/properties that are not allowed.
It defaults to reasonable subset of blacklisted Twig tags, filters, and functions, but you can customize it as you see fit:
use nystudio107\crafttwigsandbox\twig\BlacklistSecurityPolicy; use nystudio107\crafttwigsandbox\web\SandboxView; $securityPolicy = new BlacklistSecurityPolicy([ 'twigTags' => ['import'], 'twigFilters' => ['base64_decode', 'base64_encode'], 'twigFunctions' => ['dump'], ]); $sandboxView = new SandboxView(['securityPolicy' => $securityPolicy]); $result = $sandboxView->renderString("{{ dump() }}", []);
You can also control what object methods and properties are allowed to be accessed. By default, the BlacklistSecurityPolicy
does not restrict access to any object methods or properties.
For example, if you didn't want people to be able to access the password
property of the DbConfig
object via:
{{ craft.app.config.db.password }}
or
{{ craft.app.getConfig().getDb().password }}
...you would do:
use craft\config\DbConfig; use nystudio107\crafttwigsandbox\twig\BlacklistSecurityPolicy; use nystudio107\crafttwigsandbox\web\SandboxView; $securityPolicy = new BlacklistSecurityPolicy([ 'twigProperties' => [ DbConfig::class => ['password'] ], 'twigMethods' => [ DbConfig::class => ['getPassword'] ], ]); $sandboxView = new SandboxView(['securityPolicy' => $securityPolicy]); $result = $sandboxView->renderString("{{ craft.app.config.db.password }}", []);
If you don't want any properties or methods to be able to be accessed on a given object, you can pass in a *
wildcard:
'twigProperties' => [ DbConfig::class => '*' ], 'twigMethods' => [ DbConfig::class => '*' ],
WhitelistSecurityPolicy
The WhitelistSecurityPolicy
is a SecurityPolicy
that specifies the Twig tags, filters, functions, and object methods/properties that are allowed.
It defaults to reasonable subset of whitelisted Twig tags, filters, functions, and object methods/properties, but you can customize it as you see fit:
use nystudio107\crafttwigsandbox\twig\WhitelistSecurityPolicy; use nystudio107\crafttwigsandbox\web\SandboxView; $securityPolicy = new WhitelistSecurityPolicy([ 'twigTags' => ['for', 'if'], 'twigFilters' => ['replace', 'sort'], 'twigFunctions' => ['date', 'random'], ]); $sandboxView = new SandboxView(['securityPolicy' => $securityPolicy]); $result = $sandboxView->renderString("{{ dump() }}", []);
You can also control what object methods and properties are allowed to be accessed. By default, the WhitelistSecurityPolicy
restricts access to all object methods or properties.
That means you must explicitly specify each object property or method.
For example, if you wanted to grant access to:
{{ craft.app.config.general.devMode }}
or
{{ craft.app.getConfig().getGeneral().getDevMode() }}
...you would do:
use craft\config\GeneralConfig; use craft\services\Config; use craft\web\Application; use craft\web\twig\variables\CraftVariable; use nystudio107\crafttwigsandbox\twig\WhitelistSecurityPolicy; use nystudio107\crafttwigsandbox\web\SandboxView; $securityPolicy = new WhitelistSecurityPolicy([ 'twigProperties' => [ CraftVariable::class => ['app'], Application::class => ['config'], Config::class => ['general'], GeneralConfig::class => ['devMode'], ] 'twigMethods' => [ Application::class => ['getConfig'], Config::class => ['getGeneral'], ], ]); $sandboxView = new SandboxView(['securityPolicy' => $securityPolicy]); $result = $sandboxView->renderString("{{ craft.app.config.general.devMode }}", []);
If you want all properties or methods to be able to be accessed on a given object, you can pass in a *
wildcard:
'twigProperties' => [ DbConfig::class => '*' ], 'twigMethods' => [ DbConfig::class => '*' ],
SecurityPolicy from a config file
Often you'll want to provide a sane Twig sandbox, but also allow your users to add or remove from the policy as they see fit.
To make this easy to do, there is a SecurityPolicy::createFromFile()
helper method to create a sandbox security policy from a config file:
public static function createFromFile(string $filePath, ?string $alias = null): BaseSecurityPolicy
You pass it in a $filePath
, and it will look for a file of that name (with .php
added to the end of it) in the craft/config/
directory. If no file is found, it will then also try to resolve the optional $alias
and look for the file in that directory.
If the file still is not found, it will return a default BlacklistSecurityPolicy
.
The config file is a standard Yii2 Object Configuration file.
Example files you can copy & rename exists in the craft-twig-standbox
codebase in src/config/
, as blacklist-sandbox.php
and whitelist-sandbox-php
.
These are the default files that are used to create the respective security policies when you allocate a new BlacklistSecurityPolicy
or WhitelistSecurityPolicy
, and pass in no object configuration.
So for a practical example, the author of the SEOmatic plugin would copy the config/blacklist-sandbox.php
file to that plugin's src/
directory as seomatic-sandbox.php
, and put in any customizations that they might want there.
Then they could direct their users to copy the seomatic-sandbox.php
file to their craft/config/
directory if they wanted to make any customizations to it.
Then to create the sandbox view in the plugin, they would do:
use nystudio107\crafttwigsandbox\helpers\SecurityPolicy; $securityPolicy = SecurityPolicy::createFromFile('seomatic-sandbox', '@nystudio107/seomatic'); $sandboxView = new SandboxView(['securityPolicy' => $securityPolicy]);
This will cause it to create the sandbox from the seomatic-sandbox.php
file in the craft/config/
directory (if it exists), and if it does not exist, it will load the config file from the seomatic-sandbox.php
in the @nystudio107/seomatic
directory (which points to the plugin's source).
Craft automatically creates a namespaced alias for each plugin.
Custom SecurityPolicy
You can also create your own custom SecurityPolicy
to use, it just needs to conform to the Twig SecurityPolicyInterface
:
use my\custom\SecurityPolicy; use nystudio107\crafttwigsandbox\web\SandboxView; $securityPolicy = new SecurityPolicy([ ]); $sandboxView = new SandboxView(['securityPolicy' => $securityPolicy]); $result = $sandboxView->renderString("{{ dump() }}", []);
Adding a SandboxView via config/app.php
If you want to make a Twig sandbox available globally in your Craft application, you can add the following to your config/app.php
:
use craft\config\DbConfig; use nystudio107\crafttwigsandbox\twig\BlacklistSecurityPolicy; use nystudio107\crafttwigsandbox\web\SandboxView; return [ // ... 'components' => [ 'sandboxView' => [ 'class' => SandboxView::class, 'securityPolicy' => new BlacklistSecurityPolicy([ 'twigProperties' => [ DbConfig::class => '*' ], 'twigMethods' => [ DbConfig::class => '*' ], ]), ], ], ];
This will create a globally available component that you can use via:
Craft::$app->sandboxView->renderString('{% set password = craft.app.getConfig().getDb().password("") %}');
You can even globally replace the default Craft view
with a SandboxView
if you want:
return [ // ... 'components' => [ 'view' => [ 'class' => SandboxView::class, 'securityPolicy' => new BlacklistSecurityPolicy([ 'twigProperties' => [ DbConfig::class => '*' ], 'twigMethods' => [ DbConfig::class => '*' ], ]), ], ], ];
Craft Twig Sandbox Roadmap
Brought to you by nystudio107