- Soften IsSandboxed XML doc as a best-effort marker
- Replace #XXXXX placeholders with #25399
- Set Scriban MemberFilter to allowlist public properties only,
blocking method/field access and reflection escape paths
- Update Razor and Scriban safe-runtime docs to match
- Add reflection-escape, method-invocation and nested-property
tests for Scriban
The Razor template is a standard C# class, so you can freely use the functions of C#, such as `dependency injection`, using `LINQ`, custom methods, and even using `Repository`.
> ⚠ **Security notice**
>
> The Razor rendering engine compiles template content into a fully-trusted .NET assembly via Roslyn and executes it in the host process. **Editing a Razor template at runtime is functionally equivalent to executing arbitrary server-side code** — the template can access the filesystem, environment variables, the application's DI container, secrets, and any other .NET API.
>
> The framework reflects this fact through the `ITemplateRenderingEngine.IsSandboxed` property: `RazorTemplateRenderingEngine.IsSandboxed == false`. The [Text Template Management](../../../modules/text-template-management.md) module reads this flag and requires the dedicated `TextTemplateManagement.TextTemplates.EditNonSandboxedContents` permission (in addition to `EditContents`) before allowing such templates to be edited via its UI.
>
> Treat the ability to edit Razor template content as equivalent to granting shell access to the application server, and grant the related permission only to fully trusted developers/operators. If you need a sandboxed engine for content editors, consider [Scriban](scriban.md), whose templates cannot invoke arbitrary .NET APIs.
> The Razor engine compiles template content into a fully-trusted .NET assembly via Roslyn and executes it in the host process, so editing a Razor template at runtime is functionally equivalent to executing arbitrary server-side code. `RazorTemplateRenderingEngine.IsSandboxed` is therefore `false`, and the [Text Template Management](../../../modules/text-template-management.md) module requires the `TextTemplateManagement.TextTemplates.EditNonSandboxedContents` permission (in addition to `EditContents`) before allowing such templates to be edited via its UI. Grant the related permission only to fully trusted developers/operators. If you need a sandboxed engine for content editors, consider [Scriban](scriban.md), which is configured to honor Scriban's [safe runtime boundaries](https://github.com/scriban/scriban/blob/master/site/docs/runtime/safe-runtime.md) by default.
Scriban's [safe runtime](https://github.com/scriban/scriban/blob/master/site/docs/runtime/safe-runtime.md) builds the practical sandbox out of four boundaries: which globals you expose through `ScriptObject`, which .NET members you allow through the member filter, whether you configure `TemplateContext.TemplateLoader` for `include`, and which `TemplateContext` execution limits you enable. ABP's `ScribanTemplateRenderingEngine` is configured to honor these boundaries by default:
| Boundary | ABP default |
|----------|-------------|
| Globals exposed | Only the `globalContext` (`Dictionary<string,object>`) entries, the `model` you pass to `RenderAsync`, and the `L` localization helper. |
| .NET member access | `TemplateContext.MemberFilter` is set to `IsMemberAllowed`, an allowlist that exposes public properties only. Methods, fields, events, and `object`-level members (`GetType`, `ToString`, ...) are not reachable, which closes reflection-based escape paths such as `{{ model.GetType.Assembly.GetType "..." }}`. |
| `TemplateLoader` | Not configured. `include` directives have no template loader and cannot read templates from disk or other sources unless you explicitly wire one up. |
| Execution limits | Scriban's defaults (`LoopLimit = 1000`, `RecursiveLimit = 100`, `LimitToString = 1 MB`, `RegexTimeOut = 10s`). Override `CreateScribanTemplateContext` to tighten these for your own scenarios. |
The recommended way to expose data to a Scriban template is via `ScriptObject` or `IDictionary<string, object>` — the keys you put there are exactly what the template can see. When you pass a .NET object as `model`, the `MemberFilter` ensures only properties are exposed, but the safest pattern is to pre-build a dictionary or `ScriptObject` so the surface is fully under your control:
````csharp
await _templateRenderer.RenderAsync(
"MyTemplate",
model: new Dictionary<string,object>
{
{ "name", user.Name },
{ "email", user.Email }
});
````
If you must pass a .NET object whose methods/fields the template needs to read, override `ScribanTemplateRenderingEngine.IsMemberAllowed` to relax the filter. Only do so when the model objects are trusted and do not carry secrets, since methods and reflection entry points become reachable to whoever can edit the template content.
## Installation
It is suggested to use the [ABP CLI](../../../cli) to install this package.
Sandboxed engines (e.g. Scriban) interpret templates as a restricted DSL without .NET interop. Non-sandboxed engines (e.g. Razor) compile templates into fully-trusted .NET code that runs with the same privileges as the host process.
- `TemplateRenderingEngineBase` provides a virtual default of `false` (secure-by-default): engines that derive from the base class and don't override the property are treated as non-sandboxed.
- `RazorTemplateRenderingEngine` declares `IsSandboxed => false` (compiles to .NET assembly via Roslyn).
- `ScribanTemplateRenderingEngine` declares `IsSandboxed => true`(DSL with no .NET interop).
- `ScribanTemplateRenderingEngine` declares `IsSandboxed => true`and now sets Scriban's `TemplateContext.MemberFilter` so only public properties on imported objects are exposed; methods, fields, events and reflection entry points (`GetType`, `Assembly`, ...) are no longer reachable from Scriban templates.
- If your engine derives from `TemplateRenderingEngineBase`, no action is required for compilation; however, override `IsSandboxed => true` if your engine is genuinely sandboxed so callers (such as the Text Template Management module) treat its templates as safe to edit by non-developer users.
- Scriban templates that invoke methods on the model (e.g. `{{ model.SomeMethod }}`) or read fields stop working because the engine now whitelists public properties only. Templates that access only properties (the typical Scriban usage) are unaffected. To restore the previous behavior in custom hosts, derive from `ScribanTemplateRenderingEngine` and override `IsMemberAllowed` to allow methods or fields:
> See [#XXXXX](https://github.com/abpframework/abp/pull/XXXXX) for details.
Only do this when the model objects are trusted and do not carry secrets, since the previous behavior exposed reflection entry points (`GetType`, `Assembly`, ...) on imported .NET objects.
> See [#25399](https://github.com/abpframework/abp/pull/25399) for details.
### Text Template Management — `EditContents` Permission Split (security)
@ -209,11 +216,11 @@ After upgrading, audit which roles currently hold `EditContents` and decide whic
}
```
> ⚠ Do **not** automate this seeding for arbitrary tenants/roles without first reviewing the current grants — auto-restoring the previously-implicit elevated trust would defeat the security improvement. The recommended path is to grant the new permission only to specific developer/operator roles via the Permission Management UI.
> Do not automate this seeding for arbitrary tenants/roles without first reviewing the current grants — auto-restoring the previously-implicit elevated trust would defeat the security improvement. The recommended path is to grant the new permission only to specific developer/operator roles via the Permission Management UI.
If your application's data seeder grants all permissions in the `TextTemplateManagement` group to a role (e.g. the `admin` role), that role will automatically receive `EditNonSandboxedContents` on first run after upgrade. Audit your seeder if you want stricter defaults.
> See the [Razor Integration](../../framework/infrastructure/text-templating/razor.md) document and [#XXXXX](https://github.com/abpframework/abp/pull/XXXXX) for details.
> See the [Razor Integration](../../framework/infrastructure/text-templating/razor.md) document and [#25399](https://github.com/abpframework/abp/pull/25399) for details.