fix(scriptanalysis): memoise ScriptTrustValidator verdicts by code hash for the default reference set (review 2026-10-21 04/P3)

This commit is contained in:
Joseph Doherty
2026-10-22 06:18:15 -04:00
parent ae1282ed9d
commit 8c935480fa
4 changed files with 170 additions and 2 deletions
@@ -68,11 +68,26 @@ public static class ScriptTrustValidator
/// forwards its full compilation reference surface here precisely because
/// doing so is safe.
/// </param>
/// <returns>A list of trust-model violation messages; empty if the script is clean.</returns>
/// <returns>
/// A read-only list of trust-model violation messages; empty if the script is clean.
/// When <paramref name="extraReferences"/> is null or empty the verdict is memoised
/// process-wide by code hash (a repeat call returns the same instance); a call with
/// extra references always analyses afresh, since they can change resolution.
/// </returns>
public static IReadOnlyList<string> FindViolations(
string code,
IEnumerable<MetadataReference>? extraReferences = null)
=> FindViolations(code, ScriptTrustPolicy.AnalysisReferences, extraReferences);
{
var extras = extraReferences as IReadOnlyCollection<MetadataReference> ?? extraReferences?.ToList();
if (extras is { Count: > 0 } || string.IsNullOrWhiteSpace(code))
return FindViolations(code, ScriptTrustPolicy.AnalysisReferences, extras);
// Review 2026-10-21 04/P3: the default-reference verdict is a pure function of
// the code, so memoise it — the importer's preview and apply passes otherwise
// re-run a full-framework semantic analysis for every script, every time.
return TrustVerdictCache.GetOrAdd(code,
() => FindViolations(code, ScriptTrustPolicy.AnalysisReferences, extraReferences: null));
}
/// <summary>
/// Overload that runs the trust analysis against an explicit base reference