15

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:

  1. 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.

  2. 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.

  3. 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.

  4. 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.

Rob Smith
  • 173
  • 10
  • Hmm, I'm not so familiar with game scripting, but why not use V8 and Javascript? It's lightning fast and most everyone will be comfortable working with JS. – omninonsense Dec 23 '11 at 19:53
  • 1
    It really depends on what you want to achieve, if the plugins are in ruby it will be hard to prevent one of them to reopen a core class and do anything they want in the same way a ruby on rails plugin can do anything it wants. Aside of that using any other language as a plugin language is overkill, you will do more work on it than the game itself ;) – Schmurfy Dec 26 '11 at 15:38
  • If I decide to embed a language, I would probably lean toward Lua instead of Javascript (because I need an excuse to learn the former). I've edited the original post to add a bit of context on my original ideas on plugins. – Rob Smith Dec 27 '11 at 00:01
  • Thanks to everyone who answered or commented, I decided to go with a lean domain-specific language in Ruby for simplicity's sake - this is a personal project and it's rather unlikely that I'd have other users to begin with, let alone user-generated content, so I'll burn the security bridge when I come to it. – Rob Smith Jan 17 '12 at 22:53
  • What about your DSL, did it work well? Another option that was not discussed here is to use V8 through the gem called "therubyracer". Secure by default, and even faster than a Ruby "eval" in the benchmark I just did. – plang Feb 21 '12 at 09:34

2 Answers2

5

You might consider using the Shikashi gem, which allows you to create sandboxes and define a whitelist of allowed method calls on individual objects.

user2398029
  • 6,699
  • 8
  • 48
  • 80
  • Shikashi looks very interesting, more along the lines of my original intentions here. Do you (does anyone) have any experience, war stories, tips, or general comments on it? – Rob Smith Jan 03 '12 at 08:18
  • Selected as answer because it best answers the question I originally had in mind. – Rob Smith Jan 17 '12 at 22:52
  • Does Shikashi gem works with Rails 5 beta ruby 2.3? It's not installing for me. – abrocks Feb 20 '16 at 19:16
1

Consider using jRuby instead of Ruby. Java was originally implemented to support mobile code (back in the set-top days) and has a well-tested security model/implementation that could, I suspect, wrap enough of jRuby to keep user scripts/classes from creating havoc with the rest of the game system. jRuby supports embedding, as well, which may help separate the game core from the user applications, although I do not know how robust it is at this time.

And, of course, jRuby is Ruby!

toddsundsted
  • 6,225
  • 2
  • 21
  • 13
  • Good idea with jruby. But the tests I made with jruby & jsr 223 are not really successful (variable passing). Hopefully this will change in the future. – plang Feb 21 '12 at 09:37
  • Except for the fact (and I realize I have the benefit of an additional 2 years of hind side in the timing or my comment), that Java has turned out to be riddled with sandbox-breaking security holes. Of course this isn't really a Java problem so much - security of code running on one's machine is a trust nightmare, because it ultimately comes down to trust of the other programmer(s). Trusting the sandbox because it's advertised as a sandbox is perhaps better than never distrusting anyone, but it's still trust. – jefflunt Mar 10 '14 at 20:32
  • @jefflunt Under every question about code sandboxing I read the same nonsense about trust. The goal of sandboxing isn't to eliminate trust. By running a program which you haven't personally read you're trusting someone. The goal of sandboxing is to ensure that a particular string of instructions written by an expressly untrusted entity cannot do harm. You have to trust the author of the sandbox, but that's like trusting bycicle lock manufacturers vs trusting everyone walking by not to just take your bike. It's not the same question. It's a question from a whole different realm. – Lőrinc Bethlenfalvy Nov 14 '21 at 23:30