elsa-core/doc/wiki/expressions-and-scripting.md
Sipke Schoorstra 372cf33cae
refactor(auth): remove the vestigial per-author script permission plumbing (#7990)
* refactor(auth): remove the vestigial per-author script permission plumbing

#7975 is closed won't-do: authoring a workflow is a trusted act, and a
per-author gate would not change what a script can do once it runs. The host
switch stays the control, and it is per language, so an untrusted author gets
a host with the switch off rather than a permission.

That settles what the code was still half-carrying. WorkflowDefinitionScriptAuthorizationService
took a ClaimsPrincipal it never read, and could return a MissingPermission
reason nothing produced; two call sites branched on that reason to send a 403
that could not happen. The expression-descriptor endpoint kept a map from
expression type to per-author permission whose values went unused even before
the permissions were retired -- it only ever tested membership, and the
decision was always IsBrowsable. Each of these reads as an authorization gate
to anyone scanning the file, and none of them is one.

The principal, the unreachable reason, and both dead branches are gone. The
map becomes a set of the expression types the host can switch off, which is
what it was actually being used as. Behaviour is unchanged: the only failure
is a language the host disabled, which is a property of the deployment and
so a 400 naming the switch, never a 403.

PermissionNames loses ExecuteCSharpExpressions and ExecutePythonExpressions,
which existed only for that map and the test mirroring it. Five other legacy
constants there are also unreferenced but belong to other modules; they are
left alone rather than swept up here.

Two tests asserting the host-and-user case were exact duplicates of the
host-only case once the principal stopped mattering, so they go with it.

The migration guide said deployments lose per-author granularity "until
#7975 lands" and advised disabling host code until then. That promise is
withdrawn and replaced with the actual guidance.

Closes #7975

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* docs(wiki): drop the retired exec:* permissions from the scripting guide

Review found doc/wiki/expressions-and-scripting.md still telling operators
that API callers "must have the exec:csharp-expressions permission" to
author, publish, dispatch or execute workflows containing C#, and the same
for Python. Those permissions no longer exist, so the instruction cannot be
followed and describes a gate that is not there.

Both sections now say what is actually true: the host switch is the whole
control, there is no per-caller permission because a workflow runs under the
server's authority rather than the caller's, and an untrusted author gets a
host with the switch off. The switches are noted as independent, since
enabling Python while leaving C# off is a real posture.

My earlier sweep searched for the issue number rather than the permission
strings, which is why this file was missed.

Refs #7975

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-08-25 22:58:16 +02:00

6.7 KiB

Expressions And Scripting

Expressions let workflow inputs be dynamic. The base expression feature provides evaluator infrastructure; language modules add concrete evaluators, descriptors, activities, and type/function definitions.

Base Expressions

ExpressionsFeature registers:

  • IExpressionEvaluator
  • IWellKnownTypeRegistry

The base project is Elsa.Expressions. It is intentionally small and does not own language-specific runtime behavior.

Language Modules

Module Feature Evaluator Notes
Elsa.Expressions.JavaScript JavaScriptFeature Jint-backed IJavaScriptEvaluator Adds type definitions, function definitions, RunJavaScript, and FastEndpoints assembly.
Elsa.Expressions.CSharp CSharpFeature Roslyn scripting-backed ICSharpEvaluator Adds RunCSharp, descriptors, and C# options.
Elsa.Expressions.Python PythonFeature pythonnet-backed IPythonEvaluator Registers PythonGlobalInterpreterManager as a hosted service.
Elsa.Expressions.Liquid LiquidFeature Fluid-backed Liquid manager Adds Liquid filters and parser services.

JavaScript

JavaScript is the richest expression module. It registers:

  • IJavaScriptEvaluator
  • ITypeDefinitionService
  • type describers and type definition renderers
  • function definition providers
  • variable definition providers
  • RunJavaScript activity
  • TypeScript definition support
  • expression descriptors for Studio

Configuration example from Elsa.Server.Web/Program.cs:

elsa.UseJavaScript(options =>
{
    options.AllowClrAccess = true;
    options.ConfigureEngine(engine =>
    {
        engine.Execute("function greet(name) { return `Hello ${name}!`; }");
    });
});

Additional JavaScript libraries are in Elsa.Expressions.JavaScript.Libraries, including Lodash, Lodash FP, and Moment feature packages.

CSharp

CSharpFeature registers C# descriptors and ICSharpEvaluator, then adds activities from its assembly. The reference server demonstrates configuring wrappers and appending helper scripts:

elsa.UseCSharp(options =>
{
    options.AllowHostCodeExecution = true;
    options.DisableWrappers = disableVariableWrappers;
    options.AppendScript("string Greet(string name) => $\"Hello {name}!\";");
});

Roslyn C# scripting is privileged host-code execution, not a sandbox. Hosts must explicitly set CSharpOptions.AllowHostCodeExecution to true before C# expressions or RunCSharp can be authored or executed. That switch is the whole control: there is no per-caller permission, because a workflow runs under the server's authority rather than the caller's, so gating the caller never constrained what a script could do. Any author who may write workflow definitions may use C# where the switch is on. If only some of your authors are trusted with host code, give the others a host with the switch off — see #7975.

Python

PythonFeature registers pythonnet-based evaluation and configures PythonGlobalInterpreterManager as a hosted service. Python.NET execution is privileged host-code execution, not a sandbox. Python code can access host process capabilities through pythonnet and must only be enabled for trusted workflow authors.

Hosts must explicitly set PythonOptions.AllowHostCodeExecution to true before Python expressions or RunPython can be authored or executed. As with C#, that switch is the whole control and there is no per-caller permission; the switches are independent, so Python can be enabled while C# stays off. Hosts must also configure the Python DLL path or set PYTHONNET_PYDLL.

The reference server binds Scripting:Python configuration in Program.cs.

Python.NET hardening is part of the broader script execution security tracking in #7096.

Liquid

LiquidFeature registers Fluid options, parser services, expression descriptors, and built-in filters:

  • array filters
  • string filters
  • number filters
  • miscellaneous filters
  • base64
  • keys

The reference server configures the Fluid encoder to HtmlEncoder.Default.

Expression Descriptors

Expression descriptors let Studio know which expression languages are available and how to present them. Providers are registered by language features, for example:

  • JavaScriptExpressionDescriptorProvider
  • CSharpExpressionDescriptorProvider
  • PythonExpressionDescriptorProvider
  • LiquidExpressionDescriptorProvider

The API exposes descriptors under /elsa/api/descriptors/expression-descriptors.

Type Aliases

Expression modules and activity modules register type aliases through ExpressionOptions. HTTP, for example, adds aliases such as HttpRequest, HttpResponse, RouteData, FormFile, and Downloadable in HttpFeature.

ElsaScript Relationship

ElsaScript does not replace expression languages. It uses Elsa's expression providers through language prefixes such as js =>, cs =>, py =>, and liquid =>. See ElsaScript README.

Testing

Expression tests are split by concern:

Prefer unit tests for parser/evaluator behavior and integration tests when expression evaluation interacts with workflow variables, activity outputs, or designer descriptors.