InteropTests.MemberAccess.cs 7.6 KB

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