fix(core-scripting): resolve Low code-review findings (Core.Scripting-005,006,008,009,011)

- Core.Scripting-005: DependencyExtractor.HandleTagCall now recognises
  raw-string literal paths by checking the StringLiteralExpression node
  kind instead of the legacy StringLiteralToken kind.
- Core.Scripting-006: scope CompiledScriptCache failed-compile eviction
  with TryRemove(KeyValuePair) so a racing retry entry is not evicted.
- Core.Scripting-008: document the per-publish assembly accretion as an
  accepted limitation in docs/VirtualTags.md.
- Core.Scripting-009: enumerate the authoritative deny-list (namespace
  prefixes + type-granular denies) in the Phase 7 decision-#6 entry to
  match ForbiddenTypeAnalyzer.
- Core.Scripting-011: pin ScriptSandbox.Build, ScriptContext.Deadband
  boundary semantics, and end-to-end factory + companion-sink
  integration.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Joseph Doherty
2026-05-23 07:23:42 -04:00
parent 99354bfaf2
commit 0a20de728d
10 changed files with 300 additions and 16 deletions

View File

@@ -58,8 +58,13 @@ public sealed class CompiledScriptCache<TContext, TResult>
}
catch
{
// Failed compile — evict so a retry with corrected source can succeed.
_cache.TryRemove(key, out _);
// Failed compile — evict the SPECIFIC faulted Lazy instance so a retry with
// corrected source can succeed. The KeyValuePair<,> overload compares the
// value reference, so if two threads race the same bad source both observe
// the same faulted Lazy and both reach this catch, and a concurrent retry
// re-added a fresh Lazy under the same key between the two removals, the
// second removal does NOT evict the in-flight retry. (Core.Scripting-006.)
_cache.TryRemove(new KeyValuePair<string, Lazy<ScriptEvaluator<TContext, TResult>>>(key, lazy));
throw;
}
}

View File

@@ -103,8 +103,14 @@ public static class DependencyExtractor
}
var pathArg = args[0].Expression;
// Accept any string-literal expression, including raw-string forms which
// tokenize as SingleLineRawStringLiteralToken / MultiLineRawStringLiteralToken
// rather than StringLiteralToken. Checking the expression kind
// (StringLiteralExpression) covers all token kinds Roslyn assigns to literal
// strings, so a """raw""" path is harvested rather than mis-rejected as a
// dynamic path. (Core.Scripting-005.)
if (pathArg is not LiteralExpressionSyntax literal
|| !literal.Token.IsKind(SyntaxKind.StringLiteralToken))
|| !literal.IsKind(SyntaxKind.StringLiteralExpression))
{
_rejections.Add(new DependencyRejection(
Span: pathArg.Span,