Skip to content

Commit

Permalink
Hide GetType under interop with separate ExposeGetType configuration
Browse files Browse the repository at this point in the history
  • Loading branch information
lahma committed Oct 31, 2021
1 parent 6082013 commit 745a6fb
Show file tree
Hide file tree
Showing 3 changed files with 29 additions and 23 deletions.
21 changes: 7 additions & 14 deletions Jint.Tests/Runtime/InteropTests.MemberAccess.cs
Original file line number Diff line number Diff line change
Expand Up @@ -70,33 +70,26 @@ public void ShouldBeAbleToFilterMembers()
Assert.True(engine.Evaluate("m.Field2").IsString());
Assert.True(engine.Evaluate("m.Member2").IsString());
Assert.True(engine.Evaluate("m.Method2()").IsString());

// we forbid GetType by default
Assert.True(engine.Evaluate("m.GetType").IsUndefined());
}

[Fact]
public void ShouldBeAbleToHideGetType()
public void ShouldBeAbleToExposeGetType()
{
var engine = new Engine(options => options
.SetTypeResolver(new TypeResolver
{
MemberFilter = member => !Attribute.IsDefined(member, typeof(ObsoleteAttribute))
// allow unsafe access to GetType
ExposeGetType = true
})
);
engine.SetValue("m", new HiddenMembers());

Assert.True(engine.Evaluate("m.Method1").IsUndefined());
Assert.True(engine.Evaluate("m.GetType").IsCallable());

// reflection could bypass some safeguards
Assert.Equal("Method1", engine.Evaluate("m.GetType().GetMethod('Method1').Invoke(m, [])").AsString());

// but not when we forbid GetType
var hiddenGetTypeEngine = new Engine(options => options
.SetTypeResolver(new TypeResolver
{
MemberFilter = member => member.Name != nameof(GetType)
})
);
hiddenGetTypeEngine.SetValue("m", new HiddenMembers());
Assert.True(hiddenGetTypeEngine.Evaluate("m.GetType").IsUndefined());
}

[Fact]
Expand Down
2 changes: 1 addition & 1 deletion Jint/Runtime/Interop/Reflection/IndexerAccessor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ IndexerAccessor ComposeIndexerFactory(PropertyInfo candidate, Type paramType)
return null;
}

var filter = engine.Options.Interop.TypeResolver.MemberFilter;
var filter = new Func<MemberInfo, bool>(engine.Options.Interop.TypeResolver.Filter);

// default indexer wins
if (typeof(IList).IsAssignableFrom(targetType) && filter(_iListIndexer))
Expand Down
29 changes: 21 additions & 8 deletions Jint/Runtime/Interop/TypeResolver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,27 @@ namespace Jint.Runtime.Interop
/// </summary>
public sealed class TypeResolver
{
public static readonly TypeResolver Default = new TypeResolver();
public static readonly TypeResolver Default = new();

private Dictionary<ClrPropertyDescriptorFactoriesKey, ReflectionAccessor> _reflectionAccessors = new();

/// <summary>
/// Whether to expose <see cref="object.GetType"></see> which can allow bypassing allow lists.
/// Defaults to false.
/// </summary>
public bool ExposeGetType { get; set; }

/// <summary>
/// Registers a filter that determines whether given member is wrapped to interop or returned as undefined.
/// By default allows all but will also by limited by <see cref="ExposeGetType"/> configuration.
/// </summary>
public Predicate<MemberInfo> MemberFilter { get; set; } = _ => true;
/// <seealso cref="ExposeGetType"/>
public Predicate<MemberInfo> MemberFilter { get; set; }

internal bool Filter(MemberInfo m)
{
return (ExposeGetType || m.Name != nameof(GetType)) && MemberFilter(m);
}

/// <summary>
/// Gives the exposed names for a member. Allows to expose C# convention following member like IsSelected
Expand Down Expand Up @@ -97,7 +110,7 @@ internal ReflectionAccessor GetAccessor(Engine engine, Type type, string member,
{
foreach (var iprop in iface.GetProperties())
{
if (!MemberFilter(iprop))
if (!Filter(iprop))
{
continue;
}
Expand Down Expand Up @@ -130,7 +143,7 @@ internal ReflectionAccessor GetAccessor(Engine engine, Type type, string member,
{
foreach (var imethod in iface.GetMethods())
{
if (!MemberFilter(imethod))
if (!Filter(imethod))
{
continue;
}
Expand Down Expand Up @@ -165,7 +178,7 @@ internal ReflectionAccessor GetAccessor(Engine engine, Type type, string member,
var matches = new List<MethodInfo>();
foreach (var method in extensionMethods)
{
if (!MemberFilter(method))
if (!Filter(method))
{
continue;
}
Expand Down Expand Up @@ -201,7 +214,7 @@ internal ReflectionAccessor GetAccessor(Engine engine, Type type, string member,
var typeResolverMemberNameCreator = MemberNameCreator;
foreach (var p in type.GetProperties(bindingFlags))
{
if (!MemberFilter(p))
if (!Filter(p))
{
continue;
}
Expand Down Expand Up @@ -231,7 +244,7 @@ internal ReflectionAccessor GetAccessor(Engine engine, Type type, string member,
FieldInfo field = null;
foreach (var f in type.GetFields(bindingFlags))
{
if (!MemberFilter(f))
if (!Filter(f))
{
continue;
}
Expand All @@ -256,7 +269,7 @@ internal ReflectionAccessor GetAccessor(Engine engine, Type type, string member,
List<MethodInfo> methods = null;
foreach (var m in type.GetMethods(bindingFlags))
{
if (!MemberFilter(m))
if (!Filter(m))
{
continue;
}
Expand Down

0 comments on commit 745a6fb

Please sign in to comment.