chore: organize solution into module folders (Core/Server/Drivers/Client/Tooling)
Group all 69 projects into category subfolders under src/ and tests/ so the Rider Solution Explorer mirrors the module structure. Folders: Core, Server, Drivers (with a nested Driver CLIs subfolder), Client, Tooling. - Move every project folder on disk with git mv (history preserved as renames). - Recompute relative paths in 57 .csproj files: cross-category ProjectReferences, the lib/ HintPath+None refs in Driver.Historian.Wonderware, and the external mxaccessgw refs in Driver.Galaxy and its test project. - Rebuild ZB.MOM.WW.OtOpcUa.slnx with nested solution folders. - Re-prefix project paths in functional scripts (e2e, compliance, smoke SQL, integration, install). Build green (0 errors); unit tests pass. Docs left for a separate pass. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,87 @@
|
||||
using Microsoft.CodeAnalysis.CSharp.Scripting;
|
||||
using Microsoft.CodeAnalysis.Scripting;
|
||||
using ZB.MOM.WW.OtOpcUa.Core.Abstractions;
|
||||
|
||||
namespace ZB.MOM.WW.OtOpcUa.Core.Scripting;
|
||||
|
||||
/// <summary>
|
||||
/// Factory for the <see cref="ScriptOptions"/> every user script is compiled against.
|
||||
/// Implements Phase 7 plan decision #6 (read-only sandbox) by whitelisting only the
|
||||
/// assemblies + namespaces the script API needs; no <c>System.IO</c>, no
|
||||
/// <c>System.Net</c>, no <c>System.Diagnostics.Process</c>, no
|
||||
/// <c>System.Reflection</c>. Attempts to reference those types in a script fail at
|
||||
/// compile with a compiler error that points at the exact span — the operator sees
|
||||
/// the rejection before publish, not at evaluation.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// Roslyn's default <see cref="ScriptOptions"/> references <c>mscorlib</c> /
|
||||
/// <c>System.Runtime</c> transitively which pulls in every type in the BCL — this
|
||||
/// class overrides that with an explicit minimal allow-list.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// Namespaces pre-imported so scripts don't have to write <c>using</c> clauses:
|
||||
/// <c>System</c>, <c>System.Math</c>-style statics are reachable via
|
||||
/// <see cref="Math"/>, and <c>ZB.MOM.WW.OtOpcUa.Core.Abstractions</c> so scripts
|
||||
/// can name <see cref="DataValueSnapshot"/> directly.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// The sandbox cannot prevent a script from allocating unbounded memory or
|
||||
/// spinning in a tight loop — those are budget concerns, handled by the
|
||||
/// per-evaluation timeout (Stream A.4) + the test-harness (Stream F.4) that lets
|
||||
/// operators preview output before publishing.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public static class ScriptSandbox
|
||||
{
|
||||
/// <summary>
|
||||
/// Build the <see cref="ScriptOptions"/> used for every virtual-tag / alarm
|
||||
/// script. <paramref name="contextType"/> is the concrete
|
||||
/// <see cref="ScriptContext"/> subclass the globals will be of — the compiler
|
||||
/// uses its type to resolve <c>ctx.GetTag(...)</c> calls.
|
||||
/// </summary>
|
||||
public static ScriptOptions Build(Type contextType)
|
||||
{
|
||||
if (contextType is null) throw new ArgumentNullException(nameof(contextType));
|
||||
if (!typeof(ScriptContext).IsAssignableFrom(contextType))
|
||||
throw new ArgumentException(
|
||||
$"Script context type must derive from {nameof(ScriptContext)}", nameof(contextType));
|
||||
|
||||
// Allow-listed assemblies — each explicitly chosen. Adding here is a
|
||||
// plan-level decision; do not expand casually. HashSet so adding the
|
||||
// contextType's assembly is idempotent when it happens to be Core.Scripting
|
||||
// already.
|
||||
var allowedAssemblies = new HashSet<System.Reflection.Assembly>
|
||||
{
|
||||
// System.Private.CoreLib — primitives (int, double, bool, string, DateTime,
|
||||
// TimeSpan, Math, Convert, nullable<T>). Can't practically script without it.
|
||||
typeof(object).Assembly,
|
||||
// System.Linq — IEnumerable extensions (Where / Select / Sum / Average / etc.).
|
||||
typeof(System.Linq.Enumerable).Assembly,
|
||||
// Core.Abstractions — DataValueSnapshot + DriverDataType so scripts can name
|
||||
// the types they receive from ctx.GetTag.
|
||||
typeof(DataValueSnapshot).Assembly,
|
||||
// Core.Scripting itself — ScriptContext base class + Deadband static.
|
||||
typeof(ScriptContext).Assembly,
|
||||
// Serilog.ILogger — script-side logger type.
|
||||
typeof(Serilog.ILogger).Assembly,
|
||||
// Concrete context type's assembly — production contexts subclass
|
||||
// ScriptContext in Core.VirtualTags / Core.ScriptedAlarms; tests use their
|
||||
// own subclass. The globals wrapper is generic on this type so Roslyn must
|
||||
// be able to resolve it during compilation.
|
||||
contextType.Assembly,
|
||||
};
|
||||
|
||||
var allowedImports = new[]
|
||||
{
|
||||
"System",
|
||||
"System.Linq",
|
||||
"ZB.MOM.WW.OtOpcUa.Core.Abstractions",
|
||||
"ZB.MOM.WW.OtOpcUa.Core.Scripting",
|
||||
};
|
||||
|
||||
return ScriptOptions.Default
|
||||
.WithReferences(allowedAssemblies)
|
||||
.WithImports(allowedImports);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user