How To Control Who Can Write Extensions For Your MEF Application

With the MEF DirectoryCatalog, it is easy to load extensions from a given directory for a MEF Application.  The DirectoryCatalog will scan any assemblies in that directory and find MEF extensions in those assemblies.  This means that adding an extension to a MEF application can be as easy as dropping a DLL in an extensions folder.

However, some people would like to have control over what extensions are allowed in their MEF application.  For example, in a commercial application you may want to offer additional optional add-ons for sale, but not allow third party extensibility.  In these situations, our recommendation is generally to use strong name signing with your assemblies, and only allow assemblies with the correct public key to be added to the catalog.  Doing so is not too complicated, but we haven’t actually provided code showing how to do so… until now.

Below is the code for a MEF catalog which loads extensions from a directory, but ignores any assemblies where the public key doesn’t match one of the keys passed to the constructor.  You can also download the project, which also includes some simple tests.

 using System;
using System.Collections.Generic;
using System.Linq;
using System.ComponentModel.Composition.Primitives;
using System.IO;
using System.Reflection;

namespace System.ComponentModel.Composition.Hosting
{
    public class StrongNameCatalog : ComposablePartCatalog
    {
        AggregateCatalog _aggregateCatalog = new AggregateCatalog();

        public StrongNameCatalog(string path, params byte[][] trustedKeys)
        {
            foreach (var file in Directory.GetFiles(path))
            {
                AssemblyName assemblyName = null;
                try
                {
                    assemblyName = AssemblyName.GetAssemblyName(file);
                }
                catch (ArgumentException)
                {
                    //  According to MSDN, ArgumentException can be thrown
                    //  if the assembly file is invalid
                }
                catch (BadImageFormatException)
                {
                    //  Not a valid assembly
                }

                if (assemblyName != null)
                {
                    var publicKey = assemblyName.GetPublicKey();
                    if (publicKey != null)
                    {
                        bool trusted = false;
                        foreach (var trustedKey in trustedKeys)
                        {
                            if (assemblyName.GetPublicKey().SequenceEqual(trustedKey))
                            {
                                trusted = true;
                                break;
                            }
                        }

                        if (trusted)
                        {
                            _aggregateCatalog.Catalogs.Add(new AssemblyCatalog(file));
                        }
                    }
                }
            }
        }

        public override IQueryable<ComposablePartDefinition> Parts
        {
            get
            {
                return _aggregateCatalog.Parts;
            }
        }

        public override IEnumerable<Tuple<ComposablePartDefinition, ExportDefinition>>
            GetExports(ImportDefinition definition)
        {
            return _aggregateCatalog.GetExports(definition);
        }
    }
}

A note about “Security”

When people talk about this functionality, they often describe it as a “secure” catalog.  I don’t think it’s a good idea to call this a secure catalog, because in most cases it doesn’t increase security.  If you are worried about malicious code being loaded from your extension directory, a machine is probably already compromised if an attacker can put an arbitrary file there.  And if you want to prevent people from writing their own extensions to your application, the strong name catalog will help deter them, but if they are determined they can modify your application to remove the strong name check.

StrongNameCatalog.zip