v4.0.0
Cocoar.JsEval v4.0.0
Security hardening release — breaking. Unsafe-by-default JS globals are now off by default; opt-in via the corresponding builder flag. Closes the threat-model findings tracked in .local/security-untrusted-script-hardening.md (F1–F6).
Added
- Opt-in builder flags —
EnableNewObject(),EnableRequire(),EnableTimers(),EnableConsole(). Symmetrical with the existingEnableFetch()/EnableDebugMode(). EnableNewObjectAssemblyFallback(params Assembly[])— explicit allowlist forNewObject'sFindTypefallback. Additive across calls. Without it,NewObjectis alias-only.WithExecutionTimeout(TimeSpan)+WithMaxStatements(int)— defense-in-depth defaults: 10 s / 5 000 000. PassTimeout.InfiniteTimeSpan/0to disable.TranslationOptions.MaxAstDepth(default 256) onJsExpressionTranslator— depth guard that prevents host-crashingStackOverflowExceptionon deeply nested scripts.TsTranspiler.MaxParseDepth(default 128) — pre-parse paren/bracket/brace scan that rejects deeply nested input with a controlledTsTranspileExceptionbefore the embedded TS compiler (running as JS inside Jint, which amplifies stack ~10×) can exhaust the .NET stack.
Changed
GetValue<T>preserves reference identity for non-primitive types (IQueryable<T>, custom classes set viaSetValue) instead of routing through a JSON round-trip.ExecuteAsync-Catch now uses awhenfilter —Stop()-driven cancellation stays silent; timeouts (System.TimeoutException) propagate so consumers observe runaway scripts.
Removed
exit()JS global — left the engine permanently dead. Use an IIFE for early-return:(() => { if (cond) return early; … })().NewObjectAppDomain-wide assembly walk viaCocoar.Reflectensions.TypeHelper.FindType. Resolution is now strictlyTypeAliases ∪ EnableNewObjectAssemblyFallbackassemblies.
Migration
// Before (3.x — implicit defaults)
services.AddJsEval();
// After (4.0 — explicit opt-in for what your scripts actually need)
services.AddJsEval(b => b
.EnableNewObject()
.EnableNewObjectAssemblyFallback(typeof(MyDomainType).Assembly)
.EnableConsole()
.EnableTimers()
.EnableRequire());For DB-stored scripts that called exit(), rewrite to an IIFE:
// Before: if (cond) exit(); later code…
// After: (() => { if (cond) return; later code… })();