1. 18
  1.  

  2. 23

    This same article is posted on HN and there is a flurry of comments saying that this kind of usage of Lua is bad because the configuration file can take over the program and other security concerns. From reading a bunch of such comments, all spoken with the tone of “I’m an expert!”, I came to realize that these people don’t know Lua at all.

    It is a very common pattern for programs embedding Lua like this to create clamped-down environments that expose only a selected part of the language and it’s libraries to the running script. You can craft an environment with the exact functions and access you desire, and if the script tries to access something that isn’t there, well, it is an error because that function doesn’t exist.

    This is a built-in feature of the language, even though it changed API between Lua 5.1 and the more recent versions, it is still there.

    Lua is not too powerful for this, Lua has the exact power you need.

    1. 10

      Not only is it a built-in feature of the language, it’s an important part of Lua’s origins – See the paper The Evolution of Lua, particularly the section 3 about SOL and DEL.

      1. 7

        If anyone’s curious as to whether this is an exaggeration; here’s the actual code needed to sandbox the loading of a file like this so it only has access to a limited number of safe functions in LuaJIT:

        local chunk = loadfile("config.lua")
        setfenv(chunk, {add_bodies = sandbox.add_bodies, other_fn = sandbox.other_fn})
        chunk()
        

        In this case the worst you could do with malicious code is loop forever and chew up CPU. (There are ways to solve that too, but they’re not quite as trivial as the sandboxing.)

        1. 4

          That works for Lua 5.1. Lua 5.2 (or higher) changed how environments work, so there you do:

          local chunk = loadfile("config.lua","t",{ add_bodies = sandbox.add_bodies , other_fn = sandbox.other_fn })
          chunk()
          

          The “t” parameter disalllows pre-compiled Lua code (malicious bytecode is a problem in Lua).

          1. 2

            Also, if you are bullish about in process sandboxing, WASM LUA exists: https://github.com/vvanders/wasm_lua

            WASM has good capabilities to restrict memory use and you can restrict the maximum runtime.

          2. 3

            posted on HN and there is a flurry of comments […] all spoken with the tone of “I’m an expert!”

            So on-brand for the orange site.

            This is a built-in feature of the language, even though it changed API between Lua 5.1 and the more recent versions, it is still there.

            Do you know what’s the status of LuaJIT on that?

            1. 2

              Do you know what’s the status of LuaJIT on that?

              The old API can be implemented in terms of the new API: https://leafo.net/guides/setfenv-in-lua52-and-above.html

              But I’m not sure about vice versa. LuaJIT does not implement the new _ENV system of 5.2, tho it offers many other 5.2 features backported.

              1. 1

                Thanks!

              2. 1

                I’m not a LuaJIT user but I believe it works just like Lua 5.1 for that use case.

            2. 3

              Lua also provides some functionality that could make this even more serialization-y. For instance you could have a file like this:

              Body{x = 125.000, y = 70.000, x_vel = 0.000, y_vel = 1.000, mass = 80.000, rad = 4.500}
              Body{x = 175.000, y = 70.000, x_vel = 0.000, y_vel = -1.000, mass = 80.000, rad = 4.500}
              Body{x = -30.000, y = 70.000, x_vel = 0.000, y_vel = -1.000, mass = 0.500, rad = 1.000}
              Body{x = 330.000, y = 70.000, x_vel = 0.000, y_vel = 1.000, mass = 0.500, rad = 1.000}
              

              That’s a valid lua program that’s just calling the Body function on each line. Lua allows you to omit the () on functions that take a single string or table as an argument, so that’s just Body({...}). That means that this would work just as well for the loop case that they also showed:

              for i = 1, 32 do
                 for j = 1, 32 do
                    Body{x = 20 * i, y = 20 * j, mass = -0.1, rad = 2}
                 end
              end