Browse Source

Add IsSandboxed to ITemplateRenderingEngine

ITemplateRenderingEngine exposes a new IsSandboxed property so callers can
decide whether editing a template requires elevated trust.

- TemplateRenderingEngineBase provides a virtual default of false (secure-by-default)
- RazorTemplateRenderingEngine declares IsSandboxed=false (compiles to .NET assembly via Roslyn)
- ScribanTemplateRenderingEngine declares IsSandboxed=true (DSL without .NET interop)
- Razor integration docs and TextTemplateManagement docs document the implications
- Migration guide for ABP 10.4 documents the new abstraction member
pull/25399/head
maliming 4 months ago
parent
commit
e7774523de
No known key found for this signature in database GPG Key ID: A646B9CB645ECEA4
  1. 8
      docs/en/framework/infrastructure/text-templating/razor.md
  2. 16
      docs/en/modules/text-template-management.md
  3. 74
      docs/en/release-info/migration-guides/abp-10-4.md
  4. 20
      framework/src/Volo.Abp.TextTemplating.Core/Volo/Abp/TextTemplating/ITemplateRenderingEngine.cs
  5. 3
      framework/src/Volo.Abp.TextTemplating.Core/Volo/Abp/TextTemplating/TemplateRenderingEngineBase.cs
  6. 9
      framework/src/Volo.Abp.TextTemplating.Razor/Volo/Abp/TextTemplating/Razor/RazorTemplateRenderingEngine.cs
  7. 9
      framework/src/Volo.Abp.TextTemplating.Scriban/Volo/Abp/TextTemplating/Scriban/ScribanTemplateRenderingEngine.cs
  8. 29
      framework/test/Volo.Abp.TextTemplating.Razor.Tests/Volo/Abp/TextTemplating/Razor/RazorTemplateRenderingEngine_IsSandboxed_Tests.cs
  9. 29
      framework/test/Volo.Abp.TextTemplating.Scriban.Tests/Volo/Abp/TextTemplating/Scriban/ScribanTemplateRenderingEngine_IsSandboxed_Tests.cs

8
docs/en/framework/infrastructure/text-templating/razor.md

@ -10,6 +10,14 @@
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.
## Installation

16
docs/en/modules/text-template-management.md

@ -164,6 +164,22 @@ See the [connection strings](../framework/fundamentals/connection-strings.md) do
See the `TextTemplateManagementPermissions` class members for all permissions defined for this module.
The module exposes two edit-time permissions with different risk levels:
| Permission | Required to edit | Default grant |
|------------|------------------|---------------|
| `TextTemplateManagement.TextTemplates.EditContents` | Templates rendered by a sandboxed engine (e.g. Scriban). Editing such templates is safe for content editors because the engine cannot execute arbitrary .NET code. | Granted to roles that need to edit template text. |
| `TextTemplateManagement.TextTemplates.EditNonSandboxedContents` | Templates rendered by a **non-sandboxed** engine (e.g. Razor). Editing such templates is functionally equivalent to granting server-side code execution because the engine compiles the content into a .NET assembly that runs with the host process's privileges. | **Not granted to any role by default**, including `admin`. Must be granted explicitly. |
Whether a template is sandboxed is determined by `ITemplateRenderingEngine.IsSandboxed` on the engine that renders it. Editing a non-sandboxed template requires **both** `EditContents` and `EditNonSandboxedContents`.
The Text Template Management UI surfaces this distinction:
- A warning banner is rendered above the editor for non-sandboxed templates.
- The save and restore buttons are disabled when the current user lacks `EditNonSandboxedContents` for a non-sandboxed template.
> Treat `EditNonSandboxedContents` as equivalent to granting shell access to the application server. Only assign it to fully trusted developers or operators.
### Angular UI

74
docs/en/release-info/migration-guides/abp-10-4.md

@ -141,6 +141,80 @@ Configure<AbpPhoneNumberTwoFactorTokenProviderOptions>(options =>
> See [#25235](https://github.com/abpframework/abp/pull/25235) for details.
### Text Template Rendering Engine — `IsSandboxed` Marker
**Who is affected**
- Custom rendering engines that implement `Volo.Abp.TextTemplating.ITemplateRenderingEngine` directly (not deriving from `TemplateRenderingEngineBase`).
- Modules and applications that surface template editing to non-developer users (e.g. the Text Template Management module).
**What changed**
- `ITemplateRenderingEngine` exposes a new required property:
```csharp
bool IsSandboxed { get; }
```
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).
**What to do**
- If your application registers a custom engine by implementing `ITemplateRenderingEngine` directly (without deriving from `TemplateRenderingEngineBase`), add the property:
```csharp
public bool IsSandboxed => false; // or true if your engine cannot execute host code
```
- 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.
> See [#XXXXX](https://github.com/abpframework/abp/pull/XXXXX) for details.
### Text Template Management — `EditContents` Permission Split (security)
**Who is affected**
- Applications using the Text Template Management module that have granted the `TextTemplateManagement.TextTemplates.EditContents` permission to roles that are not fully trusted server administrators/developers.
- In particular, applications that use Razor templates (default for solutions referencing `Volo.Abp.TextTemplating.Razor`) and have any non-developer role with `EditContents`.
**What changed**
- A new permission `TextTemplateManagement.TextTemplates.EditNonSandboxedContents` has been added.
- Editing a template whose rendering engine is **non-sandboxed** (i.e. `ITemplateRenderingEngine.IsSandboxed == false`, e.g. Razor) now requires **both** permissions: `EditContents` and `EditNonSandboxedContents`.
- The new permission is **not granted to any role by default**, including the `admin` role. The previously implicit assumption — that `EditContents` was enough to edit Razor templates — has been corrected: editing such templates is functionally equivalent to granting server-side code execution and is now gated by an explicit permission whose name communicates that risk.
- The Text Template Management UI (MVC, Blazor) renders a security warning banner when the current template's engine is non-sandboxed, and disables the save/restore buttons when the current user lacks the new permission.
**What to do**
After upgrading, audit which roles currently hold `EditContents` and decide which of them should also be granted `EditNonSandboxedContents`:
1. Roles that should only edit sandboxed templates (e.g. Scriban, Liquid, plain HTML) need no further action — they keep editing those templates.
2. Roles that need to continue editing Razor templates must be granted `EditNonSandboxedContents` explicitly via the Permission Management page.
3. If — and only if — you have reviewed your role assignments and confirmed that every role currently holding `EditContents` is trusted to execute server-side code through Razor templates, you may seed the new permission for those roles via your `IDataSeedContributor`:
```csharp
var grants = await _permissionGrantRepository.GetListAsync(
TextTemplateManagementPermissions.TextTemplates.EditContents);
foreach (var grant in grants)
{
await _permissionManager.SetAsync(
TextTemplateManagementPermissions.TextTemplates.EditNonSandboxedContents,
grant.ProviderName,
grant.ProviderKey,
isGranted: true);
}
```
> ⚠ 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.
### Dependency Updates
**Who is affected**

20
framework/src/Volo.Abp.TextTemplating.Core/Volo/Abp/TextTemplating/ITemplateRenderingEngine.cs

@ -9,6 +9,26 @@ public interface ITemplateRenderingEngine
{
string Name { get; }
/// <summary>
/// Indicates whether this engine renders template content in a sandboxed way that
/// prevents the content from accessing the host runtime (filesystem, environment,
/// arbitrary .NET APIs, etc.).
/// <para>
/// Sandboxed engines (e.g. Scriban, Liquid) 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.
/// </para>
/// <para>
/// Implementations are required to declare this explicitly. The recommended
/// secure-by-default value is <c>false</c>: any engine that doesn't have a clear
/// sandboxing story should return <c>false</c> so callers such as the
/// TextTemplateManagement module treat its templates as requiring elevated trust
/// to edit. <see cref="TemplateRenderingEngineBase"/> provides a virtual default
/// of <c>false</c> for engines deriving from it.
/// </para>
/// </summary>
bool IsSandboxed { get; }
/// <summary>
/// Renders a text template.
/// </summary>

3
framework/src/Volo.Abp.TextTemplating.Core/Volo/Abp/TextTemplating/TemplateRenderingEngineBase.cs

@ -8,6 +8,9 @@ public abstract class TemplateRenderingEngineBase : ITemplateRenderingEngine
{
public abstract string Name { get; }
/// <inheritdoc cref="ITemplateRenderingEngine.IsSandboxed" />
public virtual bool IsSandboxed => false;
protected readonly ITemplateDefinitionManager TemplateDefinitionManager;
protected readonly ITemplateContentProvider TemplateContentProvider;
protected readonly IStringLocalizerFactory StringLocalizerFactory;

9
framework/src/Volo.Abp.TextTemplating.Razor/Volo/Abp/TextTemplating/Razor/RazorTemplateRenderingEngine.cs

@ -17,6 +17,15 @@ public class RazorTemplateRenderingEngine : TemplateRenderingEngineBase, ITransi
public const string EngineName = "Razor";
public override string Name => EngineName;
/// <inheritdoc />
/// <remarks>
/// Razor templates are compiled into .NET assemblies via Roslyn and executed in the
/// host process with full access to the BCL and DI container. They are NOT sandboxed,
/// and editing template contents is functionally equivalent to granting server-side
/// code execution.
/// </remarks>
public override bool IsSandboxed => false;
protected readonly IServiceScopeFactory ServiceScopeFactory;
public RazorTemplateRenderingEngine(

9
framework/src/Volo.Abp.TextTemplating.Scriban/Volo/Abp/TextTemplating/Scriban/ScribanTemplateRenderingEngine.cs

@ -14,6 +14,15 @@ public class ScribanTemplateRenderingEngine : TemplateRenderingEngineBase, ITran
public const string EngineName = "Scriban";
public override string Name => EngineName;
/// <inheritdoc />
/// <remarks>
/// Scriban interprets templates as a restricted DSL without direct .NET interop.
/// Templates cannot invoke arbitrary BCL types unless explicitly imported into the
/// script object by the host, so editing template content is safe for non-developer
/// users.
/// </remarks>
public override bool IsSandboxed => true;
public ScribanTemplateRenderingEngine(
ITemplateDefinitionManager templateDefinitionManager,
ITemplateContentProvider templateContentProvider,

29
framework/test/Volo.Abp.TextTemplating.Razor.Tests/Volo/Abp/TextTemplating/Razor/RazorTemplateRenderingEngine_IsSandboxed_Tests.cs

@ -0,0 +1,29 @@
using Shouldly;
using Xunit;
namespace Volo.Abp.TextTemplating.Razor;
public class RazorTemplateRenderingEngine_IsSandboxed_Tests : AbpTextTemplatingTestBase<RazorTextTemplatingTestModule>
{
private readonly RazorTemplateRenderingEngine _engine;
public RazorTemplateRenderingEngine_IsSandboxed_Tests()
{
_engine = GetRequiredService<RazorTemplateRenderingEngine>();
}
[Fact]
public void Razor_Engine_Should_Not_Be_Sandboxed()
{
// Razor templates compile into fully-trusted .NET code; editing them is
// equivalent to granting server-side code execution.
_engine.IsSandboxed.ShouldBeFalse();
}
[Fact]
public void Razor_Engine_Should_Expose_IsSandboxed_Through_Interface()
{
ITemplateRenderingEngine asInterface = _engine;
asInterface.IsSandboxed.ShouldBeFalse();
}
}

29
framework/test/Volo.Abp.TextTemplating.Scriban.Tests/Volo/Abp/TextTemplating/Scriban/ScribanTemplateRenderingEngine_IsSandboxed_Tests.cs

@ -0,0 +1,29 @@
using Shouldly;
using Xunit;
namespace Volo.Abp.TextTemplating.Scriban;
public class ScribanTemplateRenderingEngine_IsSandboxed_Tests : AbpTextTemplatingTestBase<ScribanTextTemplatingTestModule>
{
private readonly ScribanTemplateRenderingEngine _engine;
public ScribanTemplateRenderingEngine_IsSandboxed_Tests()
{
_engine = GetRequiredService<ScribanTemplateRenderingEngine>();
}
[Fact]
public void Scriban_Engine_Should_Be_Sandboxed()
{
// Scriban interprets templates as a restricted DSL without .NET interop;
// editing template content is safe for non-developer users.
_engine.IsSandboxed.ShouldBeTrue();
}
[Fact]
public void Scriban_Engine_Should_Expose_IsSandboxed_Through_Interface()
{
ITemplateRenderingEngine asInterface = _engine;
asInterface.IsSandboxed.ShouldBeTrue();
}
}
Loading…
Cancel
Save