I am currently working on a text-based game engine in Ruby, with the app separated into Ruby code in /lib and YAML data in /data, which is loaded when needed by the game. I want to allow the data files to contain basic scripts, mostly in an event/observer model. However, I also want users to be able to generate and share custom scenarios without having to worry about malicious code embedded in the script.
Addendum: My original plan was to have user-created content separated into two types, "modules" which were data-only (and thus safe) and plugins which added additional functionality (but obviously were not safe). To make an analogy to tabletop gaming, modules would be like published adventure scenarios and content, and plugins would be rulebooks containing additional rules and systems.
Sample script (syntax of course subject to change based on solution):
---
Location:
observers:
on_door_open: |
monster = spawn_monster(:goblin);
monster.add_item(random_item());
monster.hostile = true;
From a security standpoint, it would be ideal if scripting was strictly opt-in, probably through an included mixin with a little DSL, e.g.:
class Frog
include Scriptable
def jump; ... ; end # this can be called from a script
allow_scripting :jump
def ribbit; ... ; end # this cannot be called from a script
end
I've looked at three four options, but I'm not sure which is the best approach to take:
Use Ruby scripting, but in a sandbox of some kind.
Pros: Very familiar with Ruby, no need for "glue" code or issues integrating objects between languages.
Cons: Not very familiar with security issues or sandboxing, haven't found any out-of-the-box solutions that seem to fit.
Implement Embed another scripting language, e.g. Lua.
Pros: Ruby and Lua are C-based, so bindings should be reasonably simple. Lua is a reasonably popular language, so help available if I run into issues later. Secure, since any functionality I don't specifically bind will be unavailable from scripts.
Cons: Existing Ruby-Lua bindings seem to be one-way, old and poorly maintained, or both. Seems a mite dodgy to embed a scripting language inside another scripting language.
Implement a custom scripting language with Ruby interpreter. I've been experimenting with Treetop, and it shouldn't be too hard to make a simple grammar that would suffice for the scripts.
Pros: No need to embed another language. Only functionality I've specifically implemented will be available to scripts.
Cons: Overkill. "Not built here" syndrome. Probably horrible nest of bugs waiting to happen.
Implement the data files entirely in Ruby, using a domain-specific language.
Pros: Simple and easy.
Cons: No user-created data is trustable.
I am also open to other suggestions not on that list that I may not have thought of. What is the best solution to safely implement scripts embedded in data files?
Edit 2011年12月23日: Added fourth option with DSL, added "addendum" at top with additional thoughts/context.
You might consider using the Shikashi gem, which allows you to create sandboxes and define a whitelist of allowed method calls on individual objects.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With