From 6ddcd71c26f75f0d792315b833bce7b34fc2ba14 Mon Sep 17 00:00:00 2001 From: Sipke Schoorstra Date: Wed, 20 Aug 2025 10:50:56 +0200 Subject: [PATCH 1/2] Exclude blacklisted types (`string`, `object`, `Array`, `DateTime`) from workflow variable registration logic. (#6857) --- .../Handlers/ConfigureEngineWithVariableTypes.cs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/modules/Elsa.JavaScript/Handlers/ConfigureEngineWithVariableTypes.cs b/src/modules/Elsa.JavaScript/Handlers/ConfigureEngineWithVariableTypes.cs index b458522f0..dbbf0bbdb 100644 --- a/src/modules/Elsa.JavaScript/Handlers/ConfigureEngineWithVariableTypes.cs +++ b/src/modules/Elsa.JavaScript/Handlers/ConfigureEngineWithVariableTypes.cs @@ -11,12 +11,22 @@ namespace Elsa.JavaScript.Handlers; public class ConfigureEngineWithWorkflowVariableTypes(IOptions options) : INotificationHandler { + // Blacklist of types to exclude from registration. + private static readonly Type[] BlacklistedTypes = + [ + typeof(string), + typeof(object), + typeof(Array), + typeof(DateTime) + // Add more types if needed. + ]; + /// public Task HandleAsync(EvaluatingJavaScript notification, CancellationToken cancellationToken) { var engine = notification.Engine; var variableTypes = options.Value.VariableDescriptors - .Where(x => x.Type is { ContainsGenericParameters: false} && x.Type != typeof(object)) + .Where(x => x.Type is { ContainsGenericParameters: false } && !BlacklistedTypes.Contains(x.Type) && !x.Type.IsPrimitive) .Select(x => x.Type) .ToArray(); From 2175a3470a775e5a540fd803d04e89a000f26b75 Mon Sep 17 00:00:00 2001 From: Sipke Schoorstra Date: Wed, 20 Aug 2025 14:10:16 +0200 Subject: [PATCH 2/2] Refactors JavaScript type handling (#6858) * Exclude blacklisted types (`string`, `object`, `Array`, `DateTime`) from workflow variable registration logic. * Add integration tests for JavaScript evaluation and update type blacklist in `ConfigureEngineWithVariableTypes` * Disable central package transitive pinning and update Elsa Studio version to `3.5.0`. * Update Microsoft version to 9.0.8 in Directory.Packages.props * Enable central package transitive pinning in `Directory.Packages.props`. --- Directory.Packages.props | 4 +- .../ConfigureEngineWithVariableTypes.cs | 3 - .../JavaScriptAndNetTypeTest.cs | 71 +++++++++++++++++++ 3 files changed, 73 insertions(+), 5 deletions(-) create mode 100644 test/integration/Elsa.JavaScript.IntegrationTests/JavaScriptAndNetTypeTest.cs diff --git a/Directory.Packages.props b/Directory.Packages.props index 8b65fbfaa..8395c3aae 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -4,8 +4,8 @@ true - 3.5.0-preview.1092 - 9.0.7 + 3.5.0 + 9.0.8 diff --git a/src/modules/Elsa.JavaScript/Handlers/ConfigureEngineWithVariableTypes.cs b/src/modules/Elsa.JavaScript/Handlers/ConfigureEngineWithVariableTypes.cs index dbbf0bbdb..39421a931 100644 --- a/src/modules/Elsa.JavaScript/Handlers/ConfigureEngineWithVariableTypes.cs +++ b/src/modules/Elsa.JavaScript/Handlers/ConfigureEngineWithVariableTypes.cs @@ -11,13 +11,10 @@ namespace Elsa.JavaScript.Handlers; public class ConfigureEngineWithWorkflowVariableTypes(IOptions options) : INotificationHandler { - // Blacklist of types to exclude from registration. private static readonly Type[] BlacklistedTypes = [ typeof(string), typeof(object), - typeof(Array), - typeof(DateTime) // Add more types if needed. ]; diff --git a/test/integration/Elsa.JavaScript.IntegrationTests/JavaScriptAndNetTypeTest.cs b/test/integration/Elsa.JavaScript.IntegrationTests/JavaScriptAndNetTypeTest.cs new file mode 100644 index 000000000..3858ead2a --- /dev/null +++ b/test/integration/Elsa.JavaScript.IntegrationTests/JavaScriptAndNetTypeTest.cs @@ -0,0 +1,71 @@ +using System.Dynamic; +using System.Numerics; +using System.Text.Json; +using Elsa.Common.Converters; +using Elsa.Expressions.Models; +using Elsa.JavaScript.Contracts; +using Elsa.Testing.Shared; +using Microsoft.Extensions.DependencyInjection; +using Xunit; +using Xunit.Abstractions; + +namespace Elsa.JavaScript.IntegrationTests; + +/// +/// These tests ensure that we did not replace any JS types with .NET types. +/// +public class JavaScriptAndNetTypeTest +{ + private readonly IServiceProvider _serviceProvider; + private readonly IJavaScriptEvaluator _evaluator; + + public JavaScriptAndNetTypeTest(ITestOutputHelper testOutputHelper) + { + _serviceProvider = new TestApplicationBuilder(testOutputHelper).Build(); + _evaluator = _serviceProvider.GetRequiredService(); + } + + [Fact(DisplayName = "Can evaluate JavaScript that returns a string")] + public async Task ReturnsJsStringAsDotNetString() + { + var script = "return String('true')"; + var result = await EvaluateAsync(script); + + Assert.Equal("true", result); + } + + [Fact(DisplayName = "Can evaluate JavaScript that returns a string parsed into a Boolean")] + public async Task ReturnsJsStringParsedAsDotNetBoolean() + { + var script = "return String('true')"; + var result = await EvaluateAsync(script); + + Assert.True(result); + } + + [Fact(DisplayName = "Can evaluate JavaScript that constructs a JS Object")] + public async Task ReturnsJsObjectAsExpandoObject() + { + var script = "return Object({})"; + var result = await EvaluateAsync(script); + + Assert.NotNull(result); + Assert.IsType(result); + } + + [Fact(DisplayName = "Can evaluate JavaScript that returns a JS Array")] + public async Task ReturnsJsArrayAsDotNetArray() + { + var script = "return Array(1,2,3)"; + var result = await EvaluateAsync(script); + + Assert.NotNull(result); + } + + private async Task EvaluateAsync(string script) + { + var expressionExecutionContext = new ExpressionExecutionContext(_serviceProvider, new()); + var type = typeof(T); + return (T?)await _evaluator.EvaluateAsync(script, type, expressionExecutionContext); + } +}