How to Automate Testing of Medium Trust Code
Asked Answered
S

5

13

I would like to write automated tests that run in medium trust and fail if they require full trust.

I am writing a library where some functionality is only available in full trust scenarios and I want to verify that the code I wish to run in medium trust will work fine. If also want to know that if I change a class that requires full trust, that my tests will fail.

I have tried creating another AppDomain and loading the medium trust PolicyLevel, but I always get an error with assembly or its dependency could not be loaded while trying to run the cross AppDomain callback.

Is there a way to pull this off?

UPDATE: Based replies, here is what I have. Note that your class being tested must extend MarshalByRefObject. This is very limiting, but I don't see a way around it.

using System;
using System.Reflection;
using System.Security;
using System.Security.Permissions;
using Xunit;

namespace PartialTrustTest
{
    [Serializable]
    public class ClassUnderTest : MarshalByRefObject
    {
        public void PartialTrustSuccess()
        {
            Console.WriteLine( "partial trust success #1" );
        }

        public void PartialTrustFailure()
        {
            FieldInfo fi = typeof (Int32).GetField( "m_value", BindingFlags.Instance | BindingFlags.NonPublic );
            object value = fi.GetValue( 1 );
            Console.WriteLine( "value: {0}", value );
        }
    }

    public class Test
    {
        [Fact]
        public void MediumTrustWithExternalClass()
        {
            // ClassUnderTest must extend MarshalByRefObject
            var classUnderTest = MediumTrustContext.Create<ClassUnderTest>();

            classUnderTest.PartialTrustSuccess();
            Assert.Throws<FieldAccessException>( classUnderTest.PartialTrustFailure );
        }
    }

    internal static class MediumTrustContext
    {
        public static T Create<T>()
        {
            AppDomain appDomain = CreatePartialTrustDomain();
            var t = (T) appDomain.CreateInstanceAndUnwrap( typeof (T).Assembly.FullName, typeof (T).FullName );
            return t;
        }

        public static AppDomain CreatePartialTrustDomain()
        {
            var setup = new AppDomainSetup {ApplicationBase = AppDomain.CurrentDomain.BaseDirectory};
            var permissions = new PermissionSet( null );
            permissions.AddPermission( new SecurityPermission( SecurityPermissionFlag.Execution ) );
            permissions.AddPermission( new ReflectionPermission( ReflectionPermissionFlag.RestrictedMemberAccess ) );
            return AppDomain.CreateDomain( "Partial Trust AppDomain: " + DateTime.Now.Ticks, null, setup, permissions );
        }
    }
}
Saddle answered 12/6, 2009 at 15:33 Comment(0)
M
2

I just posted an article titled Partial Trust Testing with xUnit.net. It details the xUnit.net-based framework that we use on the Entity Framework team to exercise code under partial trust.

Here is an example of its usage.

public class SomeTests : MarshalByRefObject
{
    [PartialTrustFact]
    public void Partial_trust_test1()
    {
        // Runs in medium trust
    }
}

// Or...

[PartialTrustFixture]
public class MoreTests : MarshalByRefObject
{
    [Fact]
    public void Another_partial_trust_test()
    {
        // Runs in medium trust
    }
}
Maynardmayne answered 30/8, 2012 at 2:23 Comment(1)
The problem is that your code does not work anymore for most recent versions of xUnit... They have changed API too dramatically between 1.9 and 2.0, and even between 2.0 and 2.1...Unrestraint
P
7

Shamelessly stolen from How to Host a Partial Trust Sandbox – #7, but reimplemented (along with a simple test case) in F# just for kicks :-)

open System
open System.Reflection
open System.Security
open System.Security.Permissions
open System.Security.Policy

type Program() =
    inherit System.MarshalByRefObject()
    member x.PartialTrustSuccess() =
        Console.WriteLine("foo")
    member x.PartialTrustFailure() =
        let field = typeof<Int32>.GetField("m_value", BindingFlags.Instance ||| BindingFlags.NonPublic)
        let value = field.GetValue(1)
        Console.WriteLine("value: {0}", value)

[<EntryPoint>]
let main _ =
    let appDomain =
        let setup = AppDomainSetup(ApplicationBase = AppDomain.CurrentDomain.BaseDirectory)
        let permissions = PermissionSet(null)
        permissions.AddPermission(SecurityPermission(SecurityPermissionFlag.Execution)) |> ignore
        permissions.AddPermission(ReflectionPermission(ReflectionPermissionFlag.RestrictedMemberAccess)) |> ignore
        AppDomain.CreateDomain("Partial Trust AppDomain", null, setup, permissions)

    let program = appDomain.CreateInstanceAndUnwrap(
                      typeof<Program>.Assembly.FullName,
                      typeof<Program>.FullName) :?> Program

    program.PartialTrustSuccess()

    try
        program.PartialTrustFailure()
        Console.Error.WriteLine("partial trust test failed")
    with
        | :? FieldAccessException -> ()

    0

And a C# version:

using System;
using System.Reflection;
using System.Security;
using System.Security.Permissions;
using System.Security.Policy;

namespace PartialTrustTest
{
    internal class Program : MarshalByRefObject
    {
        public void PartialTrustSuccess()
        {
            Console.WriteLine("partial trust success #1");
        }

        public void PartialTrustFailure()
        {
            FieldInfo fi = typeof(Int32).GetField("m_value", BindingFlags.Instance | BindingFlags.NonPublic);
            object value = fi.GetValue(1);
            Console.WriteLine("value: {0}", value);
        }

        private static AppDomain CreatePartialTrustDomain()
        {
            AppDomainSetup setup = new AppDomainSetup() { ApplicationBase = AppDomain.CurrentDomain.BaseDirectory };
            PermissionSet permissions = new PermissionSet(null);
            permissions.AddPermission(new SecurityPermission(SecurityPermissionFlag.Execution));
            permissions.AddPermission(new ReflectionPermission(ReflectionPermissionFlag.RestrictedMemberAccess));
            return AppDomain.CreateDomain("Partial Trust AppDomain", null, setup, permissions);
        }

        static void Main(string[] args)
        {
            AppDomain appDomain = CreatePartialTrustDomain();

            Program program = (Program)appDomain.CreateInstanceAndUnwrap(
                typeof(Program).Assembly.FullName,
                typeof(Program).FullName);

            program.PartialTrustSuccess();

            try
            {
                program.PartialTrustFailure();
                Console.Error.WriteLine("!!! partial trust test failed");
            }
            catch (FieldAccessException)
            {
                Console.WriteLine("partial trust success #2");
            }
        }
    }
}
 C:\temp\PartialTrustTest\bin\Debug>PartialTrustTest.exe
 partial trust success #1
 partial trust success #2
Poisonous answered 19/11, 2009 at 7:24 Comment(1)
Tried for a while, could not get a working unit test based on your code or the source material. Have you had success in putting it into a c# unit test and get tests to pass and fail appropriately?Saddle
M
2

I just posted an article titled Partial Trust Testing with xUnit.net. It details the xUnit.net-based framework that we use on the Entity Framework team to exercise code under partial trust.

Here is an example of its usage.

public class SomeTests : MarshalByRefObject
{
    [PartialTrustFact]
    public void Partial_trust_test1()
    {
        // Runs in medium trust
    }
}

// Or...

[PartialTrustFixture]
public class MoreTests : MarshalByRefObject
{
    [Fact]
    public void Another_partial_trust_test()
    {
        // Runs in medium trust
    }
}
Maynardmayne answered 30/8, 2012 at 2:23 Comment(1)
The problem is that your code does not work anymore for most recent versions of xUnit... They have changed API too dramatically between 1.9 and 2.0, and even between 2.0 and 2.1...Unrestraint
M
1

I've posted a three-part blog post on unit testing in Medium Trust

I spin up an alternative AppDomain in a similar fashion to some answers here, but take it further by using a MarshalByRefObject to invoke the test method in the other AppDomain, meaning your test classes do not need to implement MarshalByRefObject

Part three (containing links to the other parts) is here http://boxbinary.com/2011/10/how-to-run-a-unit-test-in-medium-trust-with-nunitpart-three-umbraco-framework-testing/

Mazonson answered 21/10, 2011 at 16:29 Comment(2)
I should also point out that in the post is a downloadable base class you can use for any NUnit fixtureMazonson
that link seems to be dead - do you have a new home for the articles?Cronus
Y
0

I have never attempted to do this, but in general, to handle assembly load failures from custom AppDomains, you can use the AppDomain.AssemblyResolve event.

Although totally unrelated, here's an example of using AppDomain.AssemblyResolve.

Yet answered 12/8, 2009 at 8:38 Comment(0)
E
0

The answer depends on what your code does when someone with medium trust privilege attempts to access a full trust feature. I assume some kind of exception will be thrown.

In that case, write a unit test that runs in medium trust context, attempts to access a full trust feature, and expects the exception to be thrown. If you've never written a test like this, one common way to do it that most testing frameworks will support is this:

testMediumTrustUserWontAccessFeatureX()
{
    // set up the context of your test ...

    try
    {
        accessFullTrustFeature();
        fail("Test failed - Medium trust user can access full trust feature");
    }
    catch( SomeKindOfException e )
    {
        // Success - feature was denied to the untrusted user 
    }
}

If the exception is caught, it means your untrusted user didn't get to access the feature (and the test passes) but if the exception is never caught, the test fails (we expected an exception and didn't get it).

This is java-esque pseudo code but this pattern should work in whatever language you are using assuming it support exception handling.

Encapsulate answered 21/10, 2009 at 22:12 Comment(1)
Yes, I expect that an exception will be thrown when code that requires full trust is run. I have not been able to implement the "// set up the context of your test ..." part of the test to establish a medium trust context for the code to run in. I have written a library and I want to verify that all of its routines will run successfully in medium trust environments.Saddle

© 2022 - 2024 — McMap. All rights reserved.