ScriptStack 1.0.5
Loading...
Searching...
No Matches
ClrPolicies.cs
Go to the documentation of this file.
1using System;
2using System.Linq;
3using System.Reflection;
4
6{
10 public sealed class DenyAllClrPolicy : IClrPolicy
11 {
12 public bool IsTypeAllowed(Type t) => false;
13 public bool IsMemberAllowed(MemberInfo m) => false;
14 public bool IsCallAllowed(MethodInfo m) => false;
15 public bool IsReturnValueAllowed(object? value) => false;
16 }
17
21 public sealed class AllowAllClrPolicy : IClrPolicy
22 {
23 public bool IsTypeAllowed(Type t) => true;
24 public bool IsMemberAllowed(MemberInfo m) => true;
25 public bool IsCallAllowed(MethodInfo m) => true;
26 public bool IsReturnValueAllowed(object? value) => true;
27 }
28
35 public sealed class SafeClrPolicy : IClrPolicy
36 {
37 private static bool IsSafePrimitiveLike(Type t)
38 {
39 if (t.IsEnum) return true;
40 if (t.IsPrimitive) return true;
41 if (t == typeof(string)) return true;
42 if (t == typeof(decimal)) return true;
43 if (t == typeof(DateTime)) return true;
44 if (t == typeof(TimeSpan)) return true;
45 if (t == typeof(Guid)) return true;
46 return false;
47 }
48
49 private static bool IsBlockedNamespace(string? ns)
50 {
51 if (string.IsNullOrWhiteSpace(ns)) return false;
52
53 // Hard blocks: high-risk namespaces.
54 return ns.StartsWith("System.IO", StringComparison.Ordinal)
55 || ns.StartsWith("System.Reflection", StringComparison.Ordinal)
56 || ns.StartsWith("System.Diagnostics", StringComparison.Ordinal)
57 || ns.StartsWith("System.Runtime", StringComparison.Ordinal)
58 || ns.StartsWith("System.Threading", StringComparison.Ordinal)
59 || ns.StartsWith("System.Net", StringComparison.Ordinal)
60 || ns.StartsWith("Microsoft.Win32", StringComparison.Ordinal);
61 }
62
63 public bool IsTypeAllowed(Type t)
64 {
65 if (t == null) return false;
66
67 if (IsSafePrimitiveLike(t)) return true;
68
69 if (t.IsArray)
70 return IsTypeAllowed(t.GetElementType()!);
71
72 if (IsBlockedNamespace(t.Namespace))
73 return false;
74
75 // Allow simple collection containers of safe types
76 if (t.IsGenericType)
77 {
78 var def = t.GetGenericTypeDefinition();
79 if (def == typeof(Nullable<>))
80 return IsTypeAllowed(Nullable.GetUnderlyingType(t)!);
81
82 if (def == typeof(System.Collections.Generic.List<>)
83 || def == typeof(System.Collections.Generic.IList<>)
84 || def == typeof(System.Collections.Generic.ICollection<>)
85 || def == typeof(System.Collections.Generic.IEnumerable<>))
86 {
87 return IsTypeAllowed(t.GetGenericArguments()[0]);
88 }
89
90 if (def == typeof(System.Collections.Generic.Dictionary<,>)
91 || def == typeof(System.Collections.Generic.IDictionary<,>))
92 {
93 var ga = t.GetGenericArguments();
94 return IsTypeAllowed(ga[0]) && IsTypeAllowed(ga[1]);
95 }
96 }
97
98 // Allow non-generic IList/IDictionary, but only as containers for safe values.
99 if (typeof(System.Collections.IList).IsAssignableFrom(t)) return true;
100 if (typeof(System.Collections.IDictionary).IsAssignableFrom(t)) return true;
101
102 // Everything else: deny.
103 return false;
104 }
105
106 public bool IsMemberAllowed(MemberInfo m)
107 {
108 if (m == null) return false;
109
110 // Block obvious footguns
111 if (m.Name.Equals("GetType", StringComparison.OrdinalIgnoreCase)) return false;
112
113 var declaring = m.DeclaringType;
114 if (declaring == null || !IsTypeAllowed(declaring)) return false;
115
116 if (m is PropertyInfo p)
117 {
118 // Only allow properties that are safe to read/write
119 if (!IsTypeAllowed(p.PropertyType)) return false;
120
121 // Indexer properties are handled by index access; still ensure safe parameter types
122 foreach (var ip in p.GetIndexParameters())
123 {
124 if (!IsTypeAllowed(ip.ParameterType)) return false;
125 }
126 return true;
127 }
128
129 if (m is FieldInfo f)
130 {
131 // fields: allow only safe types
132 return IsTypeAllowed(f.FieldType);
133 }
134
135 // other member kinds are not allowed here
136 return false;
137 }
138
139 public bool IsCallAllowed(MethodInfo m)
140 {
141 if (m == null) return false;
142
143 var declaring = m.DeclaringType;
144 if (declaring == null || !IsTypeAllowed(declaring)) return false;
145
146 // Block methods that open the door to reflection/unsafe access
147 if (m.Name.Equals("GetType", StringComparison.OrdinalIgnoreCase)) return false;
148 if (m.Name.Equals("GetHashCode", StringComparison.OrdinalIgnoreCase)) return true;
149 if (m.Name.Equals("ToString", StringComparison.OrdinalIgnoreCase)) return true;
150
151 // Only allow calls with safe parameter + return types
152 if (m.ReturnType != typeof(void) && !IsTypeAllowed(m.ReturnType)) return false;
153
154 foreach (var p in m.GetParameters())
155 {
156 if (!IsTypeAllowed(p.ParameterType)) return false;
157 }
158
159 // A small allowlist for common harmless string helpers
160 if (declaring == typeof(string))
161 {
162 string[] allowed =
163 {
164 "Contains", "StartsWith", "EndsWith", "Substring", "IndexOf", "Replace",
165 "ToUpper", "ToLower", "Trim", "TrimStart", "TrimEnd", "Split"
166 };
167 return allowed.Contains(m.Name, StringComparer.OrdinalIgnoreCase);
168 }
169
170 // Collections: allow basic operations only (Count is a property, indexers handled separately)
171 if (declaring.IsGenericType && declaring.GetGenericTypeDefinition() == typeof(System.Collections.Generic.List<>))
172 {
173 string[] allowed = { "Add", "Remove", "RemoveAt", "Clear", "Contains" };
174 return allowed.Contains(m.Name, StringComparer.OrdinalIgnoreCase);
175 }
176
177 // Otherwise: deny.
178 return false;
179 }
180
181 public bool IsReturnValueAllowed(object? value)
182 {
183 if (value == null) return true;
184 if (value is NullReference) return true;
185
186 return IsTypeAllowed(value.GetType());
187 }
188
189 }
190}
Allows all CLR interop. Use only for trusted scripts.
bool IsReturnValueAllowed(object? value)
Denies all CLR interop. Default Policy!
bool IsReturnValueAllowed(object? value)
A conservative "safe" policy: allows access only to a small set of generally harmless BCL types and m...
static bool IsSafePrimitiveLike(Type t)
static bool IsBlockedNamespace(string? ns)
bool IsMemberAllowed(MemberInfo m)
bool IsReturnValueAllowed(object? value)
Policy hook for CLR interop. Implementations decide what is accessible from scripts.
Definition IClrPolicy.cs:10