* Add secret scripting integration for JavaScript Introduced a new `Elsa.Secrets.Scripting` module that provides secret management capabilities within JavaScript workflows. This includes configuring the Jint engine to use workflow variables, adding new type and variable definition providers, and integrating with existing secret management features. * Refactor secret name extraction to a separate method Moved the logic for extracting secret names from the main method to a dedicated private method `GetSecretNamesFromExpression`. This improves code readability and maintains the single responsibility principle by delegating secret name extraction to its own method. * Add input evaluation, sensitive input handling, and middleware refactor Introduced methods for evaluating activity input properties and handling inputs marked as sensitive. Refactored `ExecutionLogMiddleware` constructor for consistency. Enhanced `SendHttpRequestBase` to mark authorization inputs as potentially containing secrets. Removed obsolete entries and adjusted persistence logic for clarity. * Refactor IActivityStateProtector interface Remove unused using directives and unnecessary comments. Simplify the definition of the `ProtectedActivityStateContext` record. * Add activity state filtering mechanism Introduce an abstract filter base class, context, and result models to enable filtering of activity state. Implement a default filter manager to run these filters and apply a specific filter for obfuscating HTTP request headers. Update necessary dependencies and extension methods to integrate the new filtering functionality. * Add expired secrets management Implemented services to manage expired secrets by periodically checking and updating their status. Introduced a new hosted service to perform the sweep and configurable options for the sweep interval. Updated related classes and configurations accordingly. * Update SweepInterval in appsettings.json Changed the Secrets Management SweepInterval from 30 seconds to 4 hours. This adjustment aims to reduce the frequency of sweep operations and improve overall system performance. * Update comment to reflect configuring engine with secrets The comment was changed to better describe the handler's function, specifying that it configures the Jint engine with secrets instead of workflow variables. This clarifies the purpose and usage of the handler in the context of the code. * Remove unused inputDescriptors variable This commit removes the inputDescriptors variable, which was declared but never used in DefaultActivityExecutionMapper.cs. This helps in cleaning up the code and potentially reducing memory usage. Ensuring that all declared variables are utilized can improve code readability and maintainability.
147 lines
5.2 KiB
C#
147 lines
5.2 KiB
C#
using System.Diagnostics.CodeAnalysis;
|
|
using System.Security.Cryptography;
|
|
using System.Text;
|
|
using Acornima.Ast;
|
|
using Elsa.Expressions.Helpers;
|
|
using Elsa.Expressions.Models;
|
|
using Elsa.JavaScript.Contracts;
|
|
using Elsa.JavaScript.Helpers;
|
|
using Elsa.JavaScript.Notifications;
|
|
using Elsa.JavaScript.ObjectConverters;
|
|
using Elsa.JavaScript.Options;
|
|
using Elsa.Mediator.Contracts;
|
|
using Jint;
|
|
using Jint.Runtime.Interop;
|
|
using Microsoft.Extensions.Caching.Memory;
|
|
using Microsoft.Extensions.Configuration;
|
|
using Microsoft.Extensions.Options;
|
|
|
|
// ReSharper disable ConvertClosureToMethodGroup
|
|
namespace Elsa.JavaScript.Services;
|
|
|
|
/// <summary>
|
|
/// Provides a JavaScript evaluator using Jint.
|
|
/// </summary>
|
|
public class JintJavaScriptEvaluator(IConfiguration configuration, INotificationSender mediator, IOptions<JintOptions> scriptOptions, IMemoryCache memoryCache)
|
|
: IJavaScriptEvaluator
|
|
{
|
|
private readonly JintOptions _jintOptions = scriptOptions.Value;
|
|
|
|
/// <inheritdoc />
|
|
[RequiresUnreferencedCode("The Jint library uses reflection and can't be statically analyzed.")]
|
|
public async Task<object?> EvaluateAsync(string expression,
|
|
Type returnType,
|
|
ExpressionExecutionContext context,
|
|
ExpressionEvaluatorOptions? options = default,
|
|
Action<Engine>? configureEngine = default,
|
|
CancellationToken cancellationToken = default)
|
|
{
|
|
var engine = await GetConfiguredEngine(configureEngine, context, options, cancellationToken);
|
|
await mediator.SendAsync(new EvaluatingJavaScript(engine, context, expression), cancellationToken);
|
|
var result = ExecuteExpressionAndGetResult(engine, expression);
|
|
await mediator.SendAsync(new EvaluatedJavaScript(engine, context, expression, result), cancellationToken);
|
|
|
|
return result.ConvertTo(returnType);
|
|
}
|
|
|
|
private async Task<Engine> GetConfiguredEngine(Action<Engine>? configureEngine, ExpressionExecutionContext context, ExpressionEvaluatorOptions? options, CancellationToken cancellationToken)
|
|
{
|
|
options ??= new ExpressionEvaluatorOptions();
|
|
|
|
var engineOptions = new Jint.Options();
|
|
|
|
if (_jintOptions.AllowClrAccess)
|
|
engineOptions.AllowClr();
|
|
|
|
ConfigureClrAccess(engineOptions);
|
|
ConfigureObjectWrapper(engineOptions);
|
|
ConfigureObjectConverters(engineOptions);
|
|
|
|
engineOptions.Interop.ObjectConverters.Add(new ByteArrayConverter());
|
|
|
|
await mediator.SendAsync(new CreatingJavaScriptEngine(engineOptions, context), cancellationToken);
|
|
var engine = new Engine(engineOptions);
|
|
|
|
configureEngine?.Invoke(engine);
|
|
ConfigureArgumentGetters(engine, options);
|
|
ConfigureConfigurationAccess(engine);
|
|
_jintOptions.ConfigureEngineCallback(engine, context);
|
|
|
|
return engine;
|
|
}
|
|
|
|
private void ConfigureClrAccess(Jint.Options options)
|
|
{
|
|
if (_jintOptions.AllowClrAccess)
|
|
options.AllowClr();
|
|
}
|
|
|
|
private void ConfigureObjectWrapper(Jint.Options options)
|
|
{
|
|
options.SetWrapObjectHandler((engine, target, type) =>
|
|
{
|
|
var instance = ObjectWrapper.Create(engine, target);
|
|
|
|
if (ObjectArrayHelper.DetermineIfObjectIsArrayLikeClrCollection(target.GetType()))
|
|
instance.Prototype = engine.Intrinsics.Array.PrototypeObject;
|
|
|
|
return instance;
|
|
});
|
|
}
|
|
|
|
private void ConfigureObjectConverters(Jint.Options options)
|
|
{
|
|
options.Interop.ObjectConverters.AddRange([new ByteArrayConverter()]);
|
|
}
|
|
|
|
private void ConfigureArgumentGetters(Engine engine, ExpressionEvaluatorOptions options)
|
|
{
|
|
foreach (var argument in options.Arguments)
|
|
engine.SetValue($"get{argument.Key}", (Func<object?>)(() => argument.Value));
|
|
}
|
|
|
|
private void ConfigureConfigurationAccess(Engine engine)
|
|
{
|
|
if (_jintOptions.AllowConfigurationAccess)
|
|
engine.SetValue("getConfig", (Func<string, object?>)(name => configuration.GetSection(name).Value));
|
|
}
|
|
|
|
private object? ExecuteExpressionAndGetResult(Engine engine, string expression)
|
|
{
|
|
var preparedScript = GetOrCreatePrepareScript(expression);
|
|
var result = engine.Evaluate(preparedScript);
|
|
return result.ToObject();
|
|
}
|
|
|
|
private Prepared<Script> GetOrCreatePrepareScript(string expression)
|
|
{
|
|
var cacheKey = "jint:script:" + Hash(expression);
|
|
|
|
return memoryCache.GetOrCreate(cacheKey, entry =>
|
|
{
|
|
if (_jintOptions.ScriptCacheTimeout.HasValue)
|
|
entry.SetAbsoluteExpiration(_jintOptions.ScriptCacheTimeout.Value);
|
|
|
|
return PrepareScript(expression);
|
|
})!;
|
|
}
|
|
|
|
private Prepared<Script> PrepareScript(string expression)
|
|
{
|
|
var prepareOptions = new ScriptPreparationOptions
|
|
{
|
|
ParsingOptions = new ScriptParsingOptions
|
|
{
|
|
AllowReturnOutsideFunction = true
|
|
}
|
|
};
|
|
return Engine.PrepareScript(expression, options: prepareOptions);
|
|
}
|
|
|
|
private string Hash(string input)
|
|
{
|
|
var bytes = Encoding.UTF8.GetBytes(input);
|
|
var hash = SHA256.HashData(bytes);
|
|
return Convert.ToBase64String(hash);
|
|
}
|
|
} |