InteropTests.MemberAccess.cs 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238
  1. using System.Collections;
  2. using System.Diagnostics.CodeAnalysis;
  3. using Jint.Native;
  4. using Jint.Runtime;
  5. using Jint.Runtime.Interop;
  6. using Jint.Tests.Runtime.Domain;
  7. namespace Jint.Tests.Runtime
  8. {
  9. public partial class InteropTests
  10. {
  11. [Fact]
  12. public void ShouldHideSpecificMembers()
  13. {
  14. var engine = new Engine(options => options.SetMemberAccessor((e, target, member) =>
  15. {
  16. if (target is HiddenMembers)
  17. {
  18. if (member == nameof(HiddenMembers.Member2) || member == nameof(HiddenMembers.Method2))
  19. {
  20. return JsValue.Undefined;
  21. }
  22. }
  23. return null;
  24. }));
  25. engine.SetValue("m", new HiddenMembers());
  26. Assert.Equal("Member1", engine.Evaluate("m.Member1").ToString());
  27. Assert.Equal("undefined", engine.Evaluate("m.Member2").ToString());
  28. Assert.Equal("Method1", engine.Evaluate("m.Method1()").ToString());
  29. // check the method itself, not its invokation as it would mean invoking "undefined"
  30. Assert.Equal("undefined", engine.Evaluate("m.Method2").ToString());
  31. }
  32. [Fact]
  33. public void ShouldOverrideMembers()
  34. {
  35. var engine = new Engine(options => options.SetMemberAccessor((e, target, member) =>
  36. {
  37. if (target is HiddenMembers && member == nameof(HiddenMembers.Member1))
  38. {
  39. return "Orange";
  40. }
  41. return null;
  42. }));
  43. engine.SetValue("m", new HiddenMembers());
  44. Assert.Equal("Orange", engine.Evaluate("m.Member1").ToString());
  45. }
  46. [Fact]
  47. public void ShouldBeAbleToFilterMembers()
  48. {
  49. var engine = new Engine(options => options
  50. .SetTypeResolver(new TypeResolver
  51. {
  52. MemberFilter = member => !Attribute.IsDefined(member, typeof(ObsoleteAttribute))
  53. })
  54. );
  55. engine.SetValue("m", new HiddenMembers());
  56. Assert.True(engine.Evaluate("m.Field1").IsUndefined());
  57. Assert.True(engine.Evaluate("m.Member1").IsUndefined());
  58. Assert.True(engine.Evaluate("m.Method1").IsUndefined());
  59. Assert.True(engine.Evaluate("m.Field2").IsString());
  60. Assert.True(engine.Evaluate("m.Member2").IsString());
  61. Assert.True(engine.Evaluate("m.Method2()").IsString());
  62. // we forbid GetType by default
  63. Assert.True(engine.Evaluate("m.GetType").IsUndefined());
  64. }
  65. [Fact]
  66. public void ShouldBeAbleToExposeGetType()
  67. {
  68. var engine = new Engine(options =>
  69. {
  70. options.Interop.AllowGetType = true;
  71. options.Interop.AllowSystemReflection = true;
  72. });
  73. engine.SetValue("m", new HiddenMembers());
  74. Assert.True(engine.Evaluate("m.GetType").IsCallable);
  75. // reflection could bypass some safeguards
  76. Assert.Equal("Method1", engine.Evaluate("m.GetType().GetMethod('Method1').Invoke(m, [])").AsString());
  77. }
  78. [Fact]
  79. public void ShouldBeAbleToVaryGetTypeConfigurationBetweenEngines()
  80. {
  81. static string TestAllowGetTypeOption(bool allowGetType)
  82. {
  83. var uri = new Uri("https://github.com/sebastienros/jint");
  84. const string Input = nameof(uri) + ".GetType();";
  85. using var engine = new Engine(options => options.Interop.AllowGetType = allowGetType);
  86. engine.SetValue(nameof(uri), JsValue.FromObject(engine, uri));
  87. var result = engine.Evaluate(Input).ToString();
  88. return result;
  89. }
  90. Assert.Equal("System.Uri", TestAllowGetTypeOption(allowGetType: true));
  91. var ex = Assert.Throws<JavaScriptException>(() => TestAllowGetTypeOption(allowGetType: false));
  92. Assert.Equal("Property 'GetType' of object is not a function", ex.Message);
  93. }
  94. [Fact]
  95. public void ShouldProtectFromReflectionServiceUsage()
  96. {
  97. var engine = new Engine();
  98. engine.SetValue("m", new HiddenMembers());
  99. // we can get a type reference if it's exposed via property, bypassing GetType
  100. var type = engine.Evaluate("m.Type");
  101. Assert.IsType<ObjectWrapper>(type);
  102. var ex = Assert.Throws<InvalidOperationException>(() => engine.Evaluate("m.Type.Module.GetType().Module.GetType('System.DateTime')"));
  103. Assert.Equal("Cannot access System.Reflection namespace, check Engine's interop options", ex.Message);
  104. }
  105. [Fact]
  106. public void TypeReferenceShouldUseTypeResolverConfiguration()
  107. {
  108. var engine = new Engine(options =>
  109. {
  110. options.SetTypeResolver(new TypeResolver
  111. {
  112. MemberFilter = member => !Attribute.IsDefined(member, typeof(ObsoleteAttribute))
  113. });
  114. });
  115. engine.SetValue("EchoService", TypeReference.CreateTypeReference(engine, typeof(EchoService)));
  116. Assert.Equal("anyone there", engine.Evaluate("EchoService.Echo('anyone there')").AsString());
  117. Assert.Equal("anyone there", engine.Evaluate("EchoService.echo('anyone there')").AsString());
  118. Assert.True(engine.Evaluate("EchoService.ECHO").IsUndefined());
  119. Assert.True(engine.Evaluate("EchoService.Hidden").IsUndefined());
  120. }
  121. [Fact]
  122. public void CustomDictionaryPropertyAccessTests()
  123. {
  124. var engine = new Engine(options =>
  125. {
  126. options.AllowClr();
  127. });
  128. var dc = new CustomDictionary<float>();
  129. dc["a"] = 1;
  130. engine.SetValue("dc", dc);
  131. Assert.Equal(1, engine.Evaluate("dc.a"));
  132. Assert.Equal(1, engine.Evaluate("dc['a']"));
  133. Assert.NotEqual(JsValue.Undefined, engine.Evaluate("dc.Add"));
  134. Assert.NotEqual(0, engine.Evaluate("dc.Add"));
  135. Assert.Equal(JsValue.Undefined, engine.Evaluate("dc.b"));
  136. engine.Execute("dc.b = 5");
  137. engine.Execute("dc.Add('c', 10)");
  138. Assert.Equal(5, engine.Evaluate("dc.b"));
  139. Assert.Equal(10, engine.Evaluate("dc.c"));
  140. }
  141. [Fact]
  142. public void CanAccessBaseClassStaticFields()
  143. {
  144. var engine = new Engine(options =>
  145. {
  146. options.AllowClr();
  147. });
  148. engine.SetValue("B", TypeReference.CreateTypeReference(engine, typeof(InheritingFromClassWithStatics)));
  149. Assert.Equal(42, engine.Evaluate("B.a").AsNumber());
  150. Assert.Equal(24, engine.Evaluate("B.a = 24; B.a").AsNumber());
  151. }
  152. private class BaseClassWithStatics
  153. {
  154. public static int a = 42;
  155. }
  156. private class InheritingFromClassWithStatics : BaseClassWithStatics
  157. {
  158. }
  159. private static class EchoService
  160. {
  161. public static string Echo(string message) => message;
  162. [Obsolete]
  163. public static string Hidden(string message) => message;
  164. }
  165. private class CustomDictionary<TValue> : IDictionary<string, TValue>
  166. {
  167. readonly Dictionary<string, TValue> _dictionary = new Dictionary<string, TValue>();
  168. public TValue this[string key]
  169. {
  170. get
  171. {
  172. if (TryGetValue(key, out var val)) return val;
  173. // Normally, dictionary Item accessor throws an error when key does not exist
  174. // But this is a custom implementation
  175. return default;
  176. }
  177. set
  178. {
  179. _dictionary[key] = value;
  180. }
  181. }
  182. public ICollection<string> Keys => _dictionary.Keys;
  183. public ICollection<TValue> Values => _dictionary.Values;
  184. public int Count => _dictionary.Count;
  185. public bool IsReadOnly => false;
  186. public void Add(string key, TValue value) => _dictionary.Add(key, value);
  187. public void Add(KeyValuePair<string, TValue> item) => _dictionary.Add(item.Key, item.Value);
  188. public void Clear() => _dictionary.Clear();
  189. public bool Contains(KeyValuePair<string, TValue> item) => _dictionary.ContainsKey(item.Key);
  190. public bool ContainsKey(string key) => _dictionary.ContainsKey(key);
  191. public void CopyTo(KeyValuePair<string, TValue>[] array, int arrayIndex) => throw new NotImplementedException();
  192. public IEnumerator<KeyValuePair<string, TValue>> GetEnumerator() => _dictionary.GetEnumerator();
  193. public bool Remove(string key) => _dictionary.Remove(key);
  194. public bool Remove(KeyValuePair<string, TValue> item) => _dictionary.Remove(item.Key);
  195. public bool TryGetValue(string key, [MaybeNullWhen(false)] out TValue value) => _dictionary.TryGetValue(key, out value);
  196. IEnumerator IEnumerable.GetEnumerator() => _dictionary.GetEnumerator();
  197. }
  198. }
  199. }