Skip to content

v4.0.0

Choose a tag to compare

@windischb windischb released this 06 May 15:09
· 14 commits to develop since this release

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 existing EnableFetch() / EnableDebugMode().
  • EnableNewObjectAssemblyFallback(params Assembly[]) — explicit allowlist for NewObject's FindType fallback. Additive across calls. Without it, NewObject is alias-only.
  • WithExecutionTimeout(TimeSpan) + WithMaxStatements(int) — defense-in-depth defaults: 10 s / 5 000 000. Pass Timeout.InfiniteTimeSpan / 0 to disable.
  • TranslationOptions.MaxAstDepth (default 256) on JsExpressionTranslator — depth guard that prevents host-crashing StackOverflowException on deeply nested scripts.
  • TsTranspiler.MaxParseDepth (default 128) — pre-parse paren/bracket/brace scan that rejects deeply nested input with a controlled TsTranspileException before 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 via SetValue) instead of routing through a JSON round-trip.
  • ExecuteAsync-Catch now uses a when filter — 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; … })().
  • NewObject AppDomain-wide assembly walk via Cocoar.Reflectensions.TypeHelper.FindType. Resolution is now strictly TypeAliases ∪ EnableNewObjectAssemblyFallback assemblies.

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… })();