Safe Plugin Loader - Forbid specific namespaces (and reflection usage)?
Im trying to wire a plugin loader where people can extend the application with custom plugins. To ensure people cannot do evil things in their plugin, i want to explicitly whitelist namespaces people can use. If anything forbidden is used in a plugin .dll, i want to reject loading it.
The loading and unloading part is the easiest thing, but what about the namespace restrictions? Anyone has any idea how i can do that? Also i need to forbid the usage of .GetType or typeof() to get around reflection usage. My idea would be by either hand-parse the .dll, somehow find all namespace or function references, and evaluate this manualy as im unaware of any other method. But maybe there is?
17 Replies
if you want any level of control you cannot use precompiled code
you'd be better off building a system that compiles plugins from source and uses roslyn to analyze the syntax beforehand
Hm.. thats also an interesting approach, but i guess shipping the whole toolchain to build project requires users - in this case gamers, as the plugins are for enhancing the game with custom mechanics - to have an sdk installed?
no, you only need to add a dependency on the relevant packages
space engineers is an example of a game that does this
Oh, thats great to know 🙂 That defenitely sounds better then manualy parsing or using a decompiler
you could also consider a hybrid approach where you host a plugin website that devs submit source to and the analysis/compilation happens there
but then the security isn't actually built into the game
Im building a framework for some games, not a specific game. so where the mods are located would be configurable by the one using the framework
Ill just ensure the mod uses only allowed system namespaces, as for lists, dictionaries etc along the game api dll and no reflections
you may want to think about more granular control as well
as in whitelisting by type or even member
which would be doable with roslyn
umod is a c# modding api that has restricted namespaces, you distribute the source files and the api handles compilation like Jimmacle said
maybe worth looking at
I believe it's popular in the Rust (game) community
it started there afaik, used to be called oxide (because rust)
correct, I remember oxide 😄
also, another benefit of doing it the roslyn way is being able to publish those analyzers as a nuget package so plugin devs can use them to get identical errors in their IDE if they try to use something that isn't allowed
use a language designed around embedding
lua, for example
C# isn't designed for it, and attempts to do this, even ones built into the runtime itself (like CAS) always have issues
also that
once you've loaded and executed a plugin there's no real way to unload it
and if this is for game modding (like you said a moment ago) locking things down just pushes the people who actually want to use those things for something to a different mod loader with less restrictions
most unity modloaders these days have none whatsoever, with umod being the only notable exception? though it isn't really used often compared to BepInEx or Melonloader
whitelisting makes more sense if it's your game and you want to support modding in a safer way
see: space engineers and the fact that i built an unrestricted plugin loader to get around it
if it's your own game, then like I said, use something made for it
instead of hacking C# to do what you want
having C# but it's actually a pseudo C# where you can't use most of the APIs because you never thought about the case where somebody would want it will just annoy people that know what C# normally has
I managed to build a solution in a single c# class which uses roslyn now. So mods have to ship the code sources. I scan the source code, check used types and functionality, verify it against a whitelist, compile the assembly in memory, and load it dynamically. works 🙂 thanks for the hint about using roslyn