Add XS expression provider as lightweight alternative to Roslyn C# expressions
Implements Hyperbee.XS-based expression provider for workflows, addressing multi-tenant security concerns with Roslyn's full C# compiler by using expression trees instead of dynamic compilation.
Changes
New Module: Elsa.Expressions.Xs
- XsEvaluator: Parses XS scripts to expression trees, compiles to delegates with caching
- XsExpressionHandler: Integrates with Elsa's expression evaluation pipeline
- XsExpressionDescriptorProvider: Registers "XS Script" expression type
-
XsFeature: DI configuration with
UseXs()extension method - XsOptions: Configurable expression cache timeout, XS config callbacks
Package Dependencies
-
Hyperbee.XS1.3.3 -
Hyperbee.Xs.Extensions1.3.3
Integration Tests
8 tests covering arithmetic, variables, conditionals, boolean expressions (5 passing)
Usage
services.AddElsa(elsa =>
{
elsa.UseXs(options =>
{
options.ExpressionCacheTimeout = TimeSpan.FromHours(1);
options.ConfigureXsConfig(config => { /* customize */ });
});
});
Expression example:
var x = 10;
var y = 20;
if (x < y) { x; } else { y; }
Known Limitations
- Workflow context (globals) access not yet implemented - requires expression tree transformation
- String concatenation via
+operator unsupported (expression tree limitation) - Array initialization syntax
new int[]not recognized by XS parser
Security Impact
- Expression tree evaluation only (no code generation/assembly loading)
- Smaller attack surface vs Roslyn scripting
- Still requires process isolation for untrusted multi-tenant scenarios
[!WARNING]
Firewall rules blocked me from connecting to one or more addresses (expand for details)
I tried to connect to the following addresses, but was blocked by firewall rules:
f.feedz.io
- Triggering command:
dotnet restore src/modules/Elsa.Expressions.Xs/Elsa.Expressions.Xs.csproj --ignore-failed-sources(dns block)- Triggering command:
dotnet restore test/integration/Elsa.Xs.IntegrationTests/Elsa.Xs.IntegrationTests.csproj --ignore-failed-sources(dns block)If you need me to access, download, or install something from one of these locations, you can either:
- Configure Actions setup steps to set up my environment, which run before the firewall is enabled
- Add the appropriate URLs or hosts to the custom allowlist in this repository's Copilot coding agent settings (admins only)
Original prompt
This section details on the original issue you should resolve
<issue_title>XS / Hyperbee.XS-based expression provider as an alternative to Roslyn C# expressions</issue_title> <issue_description>Summary
Add a new expression provider that uses Hyperbee.XS as its backing engine, offering a C#‑like syntax implemented on top of expression trees instead of Roslyn scripting.
Motivation
- The existing C# expression feature uses Roslyn as the expression evaluator. Roslyn is a full C# compiler and does not attempt to provide a sandbox for untrusted code; the recommended approach is to run untrusted scripts in a separate, restricted process or container rather than in the main application process.
- In multi‑tenant, cloud‑hosted scenarios where Elsa Server is exposed to arbitrary tenant-authored expressions, it’s hard to safely expose the Roslyn-based C# expressions directly without additional isolation.
- Hyperbee.XS is a lightweight, C#‑like scripting language that generates expression trees and is designed for embedded scripting, rule engines, and DSLs, without pulling in the full Roslyn compiler pipeline. This seems like a good fit for workflow expressions and may be easier to constrain to a limited, auditable surface.
Proposal
Introduce a new module, e.g.
Elsa.Expressions.Xs(or similar), that registers an XS expression provider.The provider would:
- Parse expressions using Hyperbee.XS and compile them to delegates.
- Expose the same globals that the current C# provider does (
WorkflowInstanceId,CorrelationId,Variable,Output,Input, etc.).- Integrate with Elsa’s existing expression infrastructure so XS can be selected as an expression type from Studio and JSON definitions.
Add a configuration hook similar to
UseCSharp(...), e.g.:services.AddElsa(elsa => { elsa.UseXs(options => { // Configure allowed types/namespaces, register helper functions, etc. }); });so hosts can:
- Control which assemblies/types are visible to XS.
- Register reusable helper functions/DSL constructs exposed to expressions.
- Potentially restrict XS usage further in multi-tenant scenarios.
Naming
Some options for the expression syntax / UI label:
XSXS ScriptHyperbee XSTo avoid confusion with the existing Roslyn-based
C#expressions, I’d suggest:
- Expression type key:
XS- Display name:
XS ScriptNotes
- This is intended as an additional option, not a replacement for the current Roslyn-based C# expressions.
- Security-wise, XS would still need to be treated as untrusted code in multi-tenant environments (and combined with process/container isolation where strong tenant isolation is required), but its expression-tree focus and smaller surface area could make it easier to lock down than full C# via Roslyn.
- https://github.com/dotnet/roslyn/issues/10830?utm_source=chatgpt.com "Securely Sandboxing Roslyn Code Execution · Issue elsa-workflows/elsa-core#10830"
- https://feed.nuget.org/packages/Hyperbee.XS?utm_source=chatgpt.com "Hyperbee.XS 1.3.3"
- https://docs.elsaworkflows.io/expressions/c "C# | Elsa 3"
- https://github.com/Stillpoint-Software/hyperbee.xs "GitHub - Stillpoint-Software/hyperbee.xs: A Lightweight, Extensible Scripting Language for Expression Trees"</issue_description>
<agent_instructions>Include integration tests</agent_instructions>
Comments on the Issue (you are @copilot in this section)
- Fixes elsa-workflows/elsa-core#7053
💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.