Binary files lua-5.0.2/include/.luasafe.h.swp and lua-safe/include/.luasafe.h.swp differ
diff -urN --exclude _darcs lua-5.0.2/include/lua.h lua-safe/include/lua.h
--- lua-5.0.2/include/lua.h	2004-03-11 20:44:52.000000000 -0500
+++ lua-safe/include/lua.h	2005-04-25 12:35:17.000000000 -0400
@@ -207,6 +207,12 @@
 LUA_API int   lua_getgccount (lua_State *L);
 LUA_API void  lua_setgcthreshold (lua_State *L, int newthreshold);
 
+/* Safe functions */
+LUA_API void  lua_setsafelevel (lua_State *L, int level);
+LUA_API int   lua_getsafelevel (lua_State *L);
+LUA_API void  lua_settaint (lua_State *L, int idx, int taint);
+LUA_API int   lua_gettaint (lua_State *L, int idx);
+
 /*
 ** miscellaneous functions
 */
diff -urN --exclude _darcs lua-5.0.2/include/luasafe.h lua-safe/include/luasafe.h
--- lua-5.0.2/include/luasafe.h	1969-12-31 19:00:00.000000000 -0500
+++ lua-safe/include/luasafe.h	2005-06-02 19:39:49.000000000 -0400
@@ -0,0 +1,34 @@
+/*
+ * API-Level Safety check macros
+ * Copyright (c) 2005 Zachary P. Landau <kapheine@divineinvasion.net>
+ */
+
+#ifndef luasafe_h
+#define luasafe_h
+
+#define SAFE_TAINT(o)           ((o)->tainted = 1)
+#define SAFE_UNTAINT(o)         ((o)->tainted = 0)
+#define SAFE_IS_TAINTED(o)      ((o)->tainted == 1)
+
+extern void safe_check_load (lua_State *L, int idx);
+
+#define SAFE_CHECK_EXECUTE(L, idx) \
+    {if (lua_gettaint(L, idx) && lua_getsafelevel(L) >= 1) \
+         luaL_error(L, "safety violation: cannot execute tainted value");}
+#define SAFE_CHECK_EVAL(L, idx) \
+    {if (lua_gettaint(L, idx) && lua_getsafelevel(L) >= 1) \
+         luaL_error(L, "safety violation: cannot evaluate tainted value");}
+#define SAFE_CHECK_FILE_DATA(L, idx) \
+    {if (lua_gettaint(L, idx) && lua_getsafelevel(L) >= 1) \
+	luaL_error(L, "safety violation: cannot modify files with tainted value"); \
+     else if (lua_getsafelevel(L) >= 3) \
+         luaL_error(L, "safety violation: cannot modify files");}
+#define SAFE_CHECK_OS_EXIT(L) \
+    {if (lua_getsafelevel(L) >= 3) \
+         luaL_error(L, "safety violation: cannot call os.exit");}
+#define SAFE_CHECK_FILE_MOD(L) \
+    {if (lua_getsafelevel(L) >= 3) \
+         luaL_error(L, "safety violation: cannot modify files");}
+#define SAFE_CHECK_LOAD(L, idx) \
+    safe_check_load(L, idx);
+#endif
diff -urN --exclude _darcs lua-5.0.2/safe/Makefile lua-safe/safe/Makefile
--- lua-5.0.2/safe/Makefile	1969-12-31 19:00:00.000000000 -0500
+++ lua-safe/safe/Makefile	2005-05-04 23:06:56.000000000 -0400
@@ -0,0 +1,10 @@
+
+LUA=../bin/lua
+
+all: test
+
+test:
+	$(LUA) level0.lua
+	$(LUA) level1.lua
+	$(LUA) level2.lua
+	$(LUA) level3.lua # this will NOT return an error code
diff -urN --exclude _darcs lua-5.0.2/safe/README lua-safe/safe/README
--- lua-5.0.2/safe/README	1969-12-31 19:00:00.000000000 -0500
+++ lua-safe/safe/README	2005-06-02 16:14:32.000000000 -0400
@@ -0,0 +1,26 @@
+
+What is Lua Safe?
+-------------
+
+Lua Safe is a patch to add tainted variables and safety levels to Lua.  It is
+similar to and uses ideas from Perl and Ruby.  It was implemented as a class
+project at the Rochester Institute of Technology (www.rit.edu).   The code was
+written by Zachary P. Landau (kapheine@divineinvasion.net).  The research was
+done by Charles Lee, Heidi Morales, Ray Wallace, and Sam Won.
+
+Using Lua Safe
+--------------
+
+Lua Safe support adds the following functions: setsafelevel(num),
+getsafelevel(), taint(var), untaint(), and istainted(var).  To set the taint
+level, either call setsafelevel() with the desired safety level, or use the -T
+command line parameter.  Once this is done, safety levels will be in effect.
+
+To run the Lua Safe version of the interpreter, run 'bin/lua'.  Without
+parameters, it will give you a Lua prompt.  If you include a filename, it will
+run that file as a script.  To run the unit tests, which also show off the
+different functionality, go into the safe/ directory and type 'make'.  This
+will run all of the unit tests.  For example usage, see the files in
+safe/examples. 
+
+Date: 2005-06-02
diff -urN --exclude _darcs lua-5.0.2/safe/TODO lua-safe/safe/TODO
--- lua-5.0.2/safe/TODO	1969-12-31 19:00:00.000000000 -0500
+++ lua-safe/safe/TODO	2005-06-02 19:43:15.000000000 -0400
@@ -0,0 +1,4 @@
+* Implement per-thread safe levels
+* Taintness is removed by many operations.  For example, using many of the
+  string and math library funcions will remove taintedness.  Is there a good
+  way to handle this other than adding code all over the place?
diff -urN --exclude _darcs lua-5.0.2/safe/common.lua lua-safe/safe/common.lua
--- lua-5.0.2/safe/common.lua	1969-12-31 19:00:00.000000000 -0500
+++ lua-safe/safe/common.lua	2005-05-04 18:58:22.000000000 -0400
@@ -0,0 +1,6 @@
+
+require "lunit.lua"
+
+function safety_check(func)
+    return lunit.assert_error_match("safety violation", func)
+end
diff -urN --exclude _darcs lua-5.0.2/safe/examples/execute.lua lua-safe/safe/examples/execute.lua
--- lua-5.0.2/safe/examples/execute.lua	1969-12-31 19:00:00.000000000 -0500
+++ lua-safe/safe/examples/execute.lua	2005-05-02 22:37:54.000000000 -0400
@@ -0,0 +1,31 @@
+
+function echo_cmd()
+    io.write("\tEnter a string to call echo on: ")
+    local input = io.read()
+    local command = "echo " .. input
+    return command
+end
+
+
+print("Insecure version: ")
+local command = echo_cmd()
+print("\tCalling `" .. command .."`")
+os.execute(command)
+
+print("\n")
+
+setsafelevel(3)
+print("Safety violation version: ")
+local ret, err = pcall(function ()
+    local command = echo_cmd()
+    print("\tCalling `" .. command .."`")
+    os.execute(command)
+end)
+
+print(err)
+
+print("Handling the data: ")
+local command = echo_cmd()
+command = string.format("%q", command)
+print("\tCalling `" .. command .."`")
+os.execute(command)
diff -urN --exclude _darcs lua-5.0.2/safe/level0.lua lua-safe/safe/level0.lua
--- lua-5.0.2/safe/level0.lua	1969-12-31 19:00:00.000000000 -0500
+++ lua-safe/safe/level0.lua	2005-05-04 23:05:42.000000000 -0400
@@ -0,0 +1,28 @@
+
+require "common"
+
+local testcase = lunit.TestCase("Level 0 Testcases")
+
+function testcase.test_taint()
+    local var = 5
+    lunit.assert_false(istainted(var))
+    var = taint(var)
+    lunit.assert_true(istainted(var))
+
+    lunit.assert_false(istainted(1+2))
+
+    local exp = taint(1) + 2 + 3
+    lunit.assert_true(istainted(exp))
+
+    exp = 1 + 2 + taint(3)
+    lunit.assert_true(istainted(exp))
+
+    local exp = 1 + 2 / 3 * 4^taint(5) + -6
+    lunit.assert_true(istainted(exp))
+
+    local exp = taint("one") .. "two"
+    lunit.assert_true(istainted(exp))
+end
+
+local ret = lunit.run()
+os.exit(ret)
diff -urN --exclude _darcs lua-5.0.2/safe/level1.lua lua-safe/safe/level1.lua
--- lua-5.0.2/safe/level1.lua	1969-12-31 19:00:00.000000000 -0500
+++ lua-safe/safe/level1.lua	2005-05-04 23:05:49.000000000 -0400
@@ -0,0 +1,38 @@
+
+require "common"
+
+local testcase = lunit.TestCase("Level 1 Testcases")
+
+function testcase.test_execute()
+    safety_check(function () os.execute(taint("echo")) end)
+end
+
+function testcase.test_eval()
+    safety_check(function () loadstring(taint("print(5)")) end)
+end
+
+function testcase.load()
+    safety_check(function () load(taint("something.lua")) end)
+end
+
+function testcase.test_file()
+    safety_check(function () io.open(taint("filename")) end)
+    safety_check(function () io.open("filename", taint("w")) end)
+
+    local TMPFILE = "/tmp/safety_test.txt"
+    local f = io.open(TMPFILE, "w")
+    safety_check(function () f:write(taint("data")) end)
+    safety_check(function () f:seek("cur", taint(1234)) end)
+    safety_check(function () os.rename(taint(TMPFILE), "/tmp/something.txt") end)
+    safety_check(function () os.rename(TMPFILE, taint("/tmp/something.txt")) end)
+    safety_check(function () os.remove(taint(TMPFILE)) end)
+    f:close()
+    os.remove(TMPFILE)
+
+    safety_check(function () io.popen(taint("filename")) end)
+    safety_check(function () io.popen("filename", taint("w")) end)
+end
+
+setsafelevel(1)
+local ret = lunit.run()
+os.exit(ret)
diff -urN --exclude _darcs lua-5.0.2/safe/level2.lua lua-safe/safe/level2.lua
--- lua-5.0.2/safe/level2.lua	1969-12-31 19:00:00.000000000 -0500
+++ lua-safe/safe/level2.lua	2005-05-04 23:05:56.000000000 -0400
@@ -0,0 +1,16 @@
+
+require "common"
+
+local testcase = lunit.TestCase("Level 2 Testcases")
+
+function testcase.test_taint()
+    local var = 5
+    lunit.assert_true(istainted(var))
+    global = 5
+    lunit.assert_true(istainted(global))
+    safety_check(function () untaint(global) end)
+end
+
+setsafelevel(2)
+local ret = lunit.run()
+os.exit(ret)
diff -urN --exclude _darcs lua-5.0.2/safe/level3.lua lua-safe/safe/level3.lua
--- lua-5.0.2/safe/level3.lua	1969-12-31 19:00:00.000000000 -0500
+++ lua-safe/safe/level3.lua	2005-05-04 23:06:13.000000000 -0400
@@ -0,0 +1,29 @@
+
+require "common"
+
+local testcase = lunit.TestCase("Level 3 Testcases")
+
+function testcase.test_taint()
+    safety_check(function () taint(5) end)
+    safety_check(function () untaint(5) end)
+    safety_check(function () a = 5 end)
+    lunit.assert_pass(function () local a = 5 end)
+end
+
+function testcase.test_exit()
+    safety_check(function () os.exit(0) end)
+end
+
+function testcase.test_file_mod()
+    safety_check(function () io.open("/tmp/blah.txt") end)
+    safety_check(function () f:seek("cur", 0) end)
+    safety_check(function () f:flush() end)
+    safety_check(function () f:close() end)
+    safety_check(function () io.tmpfile() end)
+end
+
+
+f = io.tmpfile() -- open this before we go to level 3
+setsafelevel(3)
+local ret = lunit.run()
+-- os.exit(ret)  -- unfortunately, we can't do this now
diff -urN --exclude _darcs lua-5.0.2/safe/lunit.lua lua-safe/safe/lunit.lua
--- lua-5.0.2/safe/lunit.lua	1969-12-31 19:00:00.000000000 -0500
+++ lua-safe/safe/lunit.lua	2005-05-04 18:50:17.000000000 -0400
@@ -0,0 +1,698 @@
+
+--[[--------------------------------------------------------------------------
+
+    This file is part of lunit 0.3 (alpha).
+
+    For Details about lunit look at: http://www.nessie.de/mroth/lunit/
+
+    Author: Michael Roth <mroth@nessie.de>
+
+    Copyright (c) 2004 Michael Roth <mroth@nessie.de>
+
+    Permission is hereby granted, free of charge, to any person 
+    obtaining a copy of this software and associated documentation
+    files (the "Software"), to deal in the Software without restriction,
+    including without limitation the rights to use, copy, modify, merge,
+    publish, distribute, sublicense, and/or sell copies of the Software,
+    and to permit persons to whom the Software is furnished to do so,
+    subject to the following conditions:
+
+    The above copyright notice and this permission notice shall be 
+    included in all copies or substantial portions of the Software.
+
+    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+    IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+    CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+    TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+    SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+--]]--------------------------------------------------------------------------
+
+
+
+
+-----------------------
+-- Intialize package --
+-----------------------
+
+local P = { }
+lunit = P
+
+-- Import
+local type = type
+local print = print
+local ipairs = ipairs
+local pairs = pairs
+local string = string
+local table = table
+local pcall = pcall
+local xpcall = xpcall
+local traceback = debug.traceback
+local error = error
+local setmetatable = setmetatable
+local rawset = rawset
+local orig_assert = assert
+local getfenv = getfenv
+local setfenv = setfenv
+local tostring = tostring
+
+
+-- Start package scope
+setfenv(1, P)
+
+
+
+
+--------------------------------
+-- Private data and functions --
+--------------------------------
+
+local run_testcase
+local do_assert, check_msg
+local stats = { }
+local testcases = { }
+local stats_inc, tc_mt
+
+
+
+
+--------------------------
+-- Type check functions --
+--------------------------
+
+function is_nil(x)
+  return type(x) == "nil"
+end
+
+function is_boolean(x)
+  return type(x) == "boolean"
+end
+
+function is_number(x)
+  return type(x) == "number"
+end
+
+function is_string(x)
+  return type(x) == "string"
+end
+
+function is_table(x)
+  return type(x) == "table"
+end
+
+function is_function(x)
+  return type(x) == "function"
+end
+
+function is_thread(x)
+  return type(x) == "thread"
+end
+
+function is_userdata(x)
+  return type(x) == "userdata"
+end
+
+
+
+
+----------------------
+-- Assert functions --
+----------------------
+
+function assert(assertion, msg)
+  stats_inc("assertions")
+  check_msg("assert", msg)
+  do_assert(not not assertion, "assertion failed (was: "..tostring(assertion)..")", msg)		-- (convert assertion to bool)
+  return assertion
+end
+
+
+function assert_fail(msg)
+  stats_inc("assertions")
+  check_msg("assert_fail", msg)
+  do_assert(false, "failure", msg)
+end
+
+
+function assert_true(actual, msg)
+  stats_inc("assertions")
+  check_msg("assert_true", msg)
+  do_assert(is_boolean(actual), "true expected but was a "..type(actual), msg)
+  do_assert(actual == true, "true expected but was false", msg)
+  return actual
+end
+
+
+function assert_false(actual, msg)
+  stats_inc("assertions")
+  check_msg("assert_false", msg)
+  do_assert(is_boolean(actual), "false expected but was a "..type(actual), msg)
+  do_assert(actual == false, "false expected but was true", msg)
+  return actual
+end
+
+
+function assert_equal(expected, actual, msg)
+  stats_inc("assertions")
+  check_msg("assert_equal", msg)
+  do_assert(expected == actual, "expected '"..tostring(expected).."' but was '"..tostring(actual).."'", msg)
+  return actual
+end
+
+
+function assert_not_equal(unexpected, actual, msg)
+  stats_inc("assertions")
+  check_msg("assert_not_equal", msg)
+  do_assert(unexpected ~= actual, "'"..tostring(expected).."' not expected but was one", msg)
+  return actual
+end
+
+
+function assert_match(pattern, actual, msg)
+  stats_inc("assertions")
+  check_msg("assert_match", msg)
+  do_assert(is_string(pattern), "assert_match expects the pattern as a string")
+  do_assert(is_string(actual), "expected a string to match pattern '"..pattern.."' but was a '"..type(actual).."'", msg)
+  do_assert(not not string.find(actual, pattern), "expected '"..actual.."' to match pattern '"..pattern.."' but doesn't", msg)
+  return actual
+end
+
+
+function assert_not_match(pattern, actual, msg)
+  stats_inc("assertions")
+  check_msg("assert_not_match", msg)
+  do_assert(is_string(actual), "expected a string to not match pattern '"..pattern.."' but was a '"..type(actual).."'", msg)
+  do_assert(string.find(actual, pattern) == nil, "expected '"..actual.."' to not match pattern '"..pattern.."' but it does", msg)
+  return actual
+end
+
+
+function assert_nil(actual, msg)
+  stats_inc("assertions")
+  check_msg("assert_nil", msg)
+  do_assert(is_nil(actual), "nil expected but was a "..type(actual), msg)
+  return actual
+end
+
+
+function assert_not_nil(actual, msg)
+  stats_inc("assertions")
+  check_msg("assert_not_nil", msg)
+  do_assert(not is_nil(actual), "nil not expected but was one", msg)
+  return actual
+end
+
+
+function assert_boolean(actual, msg)
+  stats_inc("assertions")
+  check_msg("assert_boolean", msg)
+  do_assert(is_boolean(actual), "boolean expected but was a "..type(actual), msg)
+  return actual
+end
+
+
+function assert_not_boolean(actual, msg)
+  stats_inc("assertions")
+  check_msg("assert_not_boolean", msg)
+  do_assert(not is_boolean(actual), "boolean not expected but was one", msg)
+  return actual
+end
+
+
+function assert_number(actual, msg)
+  stats_inc("assertions")
+  check_msg("assert_number", msg)
+  do_assert(is_number(actual), "number expected but was a "..type(actual), msg)
+  return actual
+end
+
+
+function assert_not_number(actual, msg)
+  stats_inc("assertions")
+  check_msg("assert_not_number", msg)
+  do_assert(not is_number(actual), "number not expected but was one", msg)
+  return actual
+end
+
+
+function assert_string(actual, msg)
+  stats_inc("assertions")
+  check_msg("assert_string", msg)
+  do_assert(is_string(actual), "string expected but was a "..type(actual), msg)
+  return actual
+end
+
+
+function assert_not_string(actual, msg)
+  stats_inc("assertions")
+  check_msg("assert_not_string", msg)
+  do_assert(not is_string(actual), "string not expected but was one", msg)
+  return actual
+end
+
+
+function assert_table(actual, msg)
+  stats_inc("assertions")
+  check_msg("assert_table", msg)
+  do_assert(is_table(actual), "table expected but was a "..type(actual), msg)
+  return actual
+end
+
+
+function assert_not_table(actual, msg)
+  stats_inc("assertions")
+  check_msg("assert_not_table", msg)
+  do_assert(not is_table(actual), "table not expected but was one", msg)
+  return actual
+end
+
+
+function assert_function(actual, msg)
+  stats_inc("assertions")
+  check_msg("assert_function", msg)
+  do_assert(is_function(actual), "function expected but was a "..type(actual), msg)
+  return actual
+end
+
+
+function assert_not_function(actual, msg)
+  stats_inc("assertions")
+  check_msg("assert_not_function", msg)
+  do_assert(not is_function(actual), "function not expected but was one", msg)
+  return actual
+end
+
+
+function assert_thread(actual, msg)
+  stats_inc("assertions")
+  check_msg("assert_thread", msg)
+  do_assert(is_thread(actual), "thread expected but was a "..type(actual), msg)
+  return actual
+end
+
+
+function assert_not_thread(actual, msg)
+  stats_inc("assertions")
+  check_msg("assert_not_thread", msg)
+  do_assert(not is_thread(actual), "thread not expected but was one", msg)
+  return actual
+end
+
+
+function assert_userdata(actual, msg)
+  stats_inc("assertions")
+  check_msg("assert_userdata", msg)
+  do_assert(is_userdata(actual), "userdata expected but was a "..type(actual), msg)
+  return actual
+end
+
+
+function assert_not_userdata(actual, msg)
+  stats_inc("assertions")
+  check_msg("assert_not_userdata", msg)
+  do_assert(not is_userdata(actual), "userdata not expected but was one", msg)
+  return actual
+end
+
+
+function assert_error(msg, func)
+  stats_inc("assertions")
+  if is_nil(func) then func, msg = msg, nil end
+  check_msg("assert_error", msg)
+  do_assert(is_function(func), "assert_error expects a function as the last argument but it was a "..type(func))
+  local ok, errmsg = pcall(func)
+  do_assert(ok == false, "error expected but no error occurred", msg)
+end
+
+
+function assert_error_match(msg, ret, func)
+    stats_inc("assertions")
+    if is_nil(func) then msg, ret, func = nil, msg, ret end
+    check_msg("assert_error", msg)
+    do_assert(is_function(func), "assert_error expects a function as the last argument but it was a "..type(func))
+    local ok, errmsg = pcall(func)
+    do_assert(ok == false, "error expected but no error occurred", msg)
+    do_assert(string.find(errmsg, ret) ~= nil, "error return code was not correct: `"..errmsg.."`", msg)
+end
+
+function assert_pass(msg, func)
+  stats_inc("assertions")
+  if is_nil(func) then func, msg = msg, nil end
+  check_msg("assert_pass", msg)
+  do_assert(is_function(func), "assert_pass expects a function as the last argument but it was a "..type(func))
+  local ok, errmsg = pcall(func)
+  if not ok then do_assert(ok == true, "no error expected but error was: "..errmsg, msg) end
+end
+
+
+
+
+-----------------------------------------------------------
+-- Assert implementation that assumes it was called from --
+-- lunit code which was called directly from user code.  --
+-----------------------------------------------------------
+
+function do_assert(assertion, base_msg, user_msg)
+  orig_assert(is_boolean(assertion))
+  orig_assert(is_string(base_msg))
+  orig_assert(is_string(user_msg) or is_nil(user_msg))
+  if not assertion then
+    if user_msg then
+      error(base_msg..": "..user_msg, 3)
+    else
+      error(base_msg.."!", 3)
+    end
+  end
+end
+
+-------------------------------------------
+-- Checks the msg argument in assert_xxx --
+-------------------------------------------
+
+function check_msg(name, msg)
+  orig_assert(is_string(name))
+  if not (is_nil(msg) or is_string(msg)) then
+    error("lunit."..name.."() expects the optional message as a string but it was a "..type(msg).."!" ,3)
+  end
+end
+
+
+
+
+-------------------------------------
+-- Creates a new TestCase 'Object' --
+-------------------------------------
+
+function TestCase(name)
+  do_assert(is_string(name), "lunit.TestCase() needs a string as an argument")
+  local tc = {
+    __lunit_name = name;
+    __lunit_setup = nil;
+    __lunit_tests = { };
+    __lunit_teardown = nil;
+  }
+  setmetatable(tc, tc_mt)
+  table.insert(testcases, tc)
+  return tc
+end
+
+tc_mt = {
+  __newindex = function(tc, key, value)
+    rawset(tc, key, value)
+    if is_string(key) and is_function(value) then
+      local name = string.lower(key)
+      if string.find(name, "^test") or string.find(name, "test$") then
+        table.insert(tc.__lunit_tests, key)
+      elseif name == "setup" then
+        tc.__lunit_setup = value
+      elseif name == "teardown" then
+        tc.__lunit_teardown = value
+      end
+    end
+  end
+}
+
+
+
+-----------------------------------------
+-- Wrap Functions in a TestCase object --
+-----------------------------------------
+
+function wrap(name, ...)
+  if is_function(name) then
+    table.insert(arg, 1, name)
+    name = "Anonymous Testcase"
+  end
+  
+  local tc = TestCase(name)
+  for index, test in ipairs(arg) do
+    tc["Test #"..tostring(index)] = test
+  end
+  return tc
+end
+
+
+
+
+
+
+----------------------------------
+-- Runs the complete Test Suite --
+----------------------------------
+
+function run()
+  
+  ---------------------------
+  -- Initialize statistics --
+  ---------------------------
+  
+  stats.testcases = 0	-- Total number of Test Cases
+  stats.tests = 0	-- Total number of all Tests in all Test Cases
+  stats.run = 0		-- Number of Tests run
+  stats.notrun = 0	-- Number of Tests not run
+  stats.failed = 0	-- Number of Tests failed
+  stats.passed = 0	-- Number of Test passed
+  stats.assertions = 0	-- Number of all assertions made in all Test in all Test Cases
+  
+  --------------------------------
+  -- Count Test Cases and Tests --
+  --------------------------------
+  
+  stats.testcases = table.getn(testcases)
+  
+  for _, tc in ipairs(testcases) do
+    stats_inc("tests" , table.getn(tc.__lunit_tests))
+  end
+  
+  ------------------
+  -- Print Header --
+  ------------------
+  
+  print()
+  print("#### Test Suite with "..stats.tests.." Tests in "..stats.testcases.." Test Cases loaded.")
+  
+  ------------------------
+  -- Run all Test Cases --
+  ------------------------
+  
+  for _, tc in ipairs(testcases) do
+    run_testcase(tc)
+  end
+  
+  ------------------
+  -- Print Footer --
+  ------------------
+  
+  print()
+  print("#### Test Suite finished.")
+  
+  local msg_assertions = stats.assertions.." Assertions checked. "
+  local msg_passed     = stats.passed == stats.tests and "All Tests passed" or  stats.passed.." Tests passed"
+  local msg_failed     = stats.failed > 0 and ", "..stats.failed.." failed" or ""
+  local msg_run	       = stats.notrun > 0 and ", "..stats.notrun.." not run" or ""
+  
+  print()
+  print(msg_assertions..msg_passed..msg_failed..msg_run.."!")
+  
+  -----------------
+  -- Return code --
+  -----------------
+  
+  if stats.passed == stats.tests then
+    return 0
+  else
+    return 1
+  end
+end
+
+
+
+
+-----------------------------
+-- Runs a single Test Case --
+-----------------------------
+
+function run_testcase(tc)
+  
+  orig_assert(is_table(tc))
+  orig_assert(is_table(tc.__lunit_tests))
+  orig_assert(is_string(tc.__lunit_name))
+  orig_assert(is_nil(tc.__lunit_setup) or is_function(tc.__lunit_setup))
+  orig_assert(is_nil(tc.__lunit_teardown) or is_function(tc.__lunit_teardown))
+  
+  --------------------------------------------
+  -- Protected call to a Test Case function --
+  --------------------------------------------
+  
+  local function call(errprefix, func)
+    orig_assert(is_string(errprefix))
+    orig_assert(is_function(func))
+    local ok, errmsg = xpcall(function() func(tc) end, traceback)
+    if not ok then
+      print()
+      print(errprefix..": "..errmsg)
+    end
+    return ok
+  end
+  
+  ------------------------------------
+  -- Calls setup() on the Test Case --
+  ------------------------------------
+  
+  local function setup()
+    if tc.__lunit_setup then 
+      return call("ERROR: setup() failed", tc.__lunit_setup)
+    else
+      return true
+    end
+  end
+  
+  ------------------------------------------
+  -- Calls a single Test on the Test Case --
+  ------------------------------------------
+  
+  local function run(testname)
+    orig_assert(is_string(testname))
+    orig_assert(is_function(tc[testname]))
+    local ok = call("FAIL: "..testname, tc[testname])
+    if not ok then
+      stats_inc("failed")
+    else
+      stats_inc("passed")
+    end
+    return ok
+  end
+  
+  ---------------------------------------
+  -- Calls teardown() on the Test Case --
+  ---------------------------------------
+  
+  local function teardown()
+     if tc.__lunit_teardown then
+       call("WARNING: teardown() failed", tc.__lunit_teardown)
+     end
+  end
+  
+  ---------------------------------
+  -- Run all Tests on a TestCase --
+  ---------------------------------
+  
+  print()
+  print("#### Running '"..tc.__lunit_name.."' ("..table.getn(tc.__lunit_tests).." Tests)...")
+  
+  for _, testname in ipairs(tc.__lunit_tests) do
+    if setup() then
+      run(testname)
+      stats_inc("run")
+      teardown()
+    else
+      print("WARN: Skipping '"..testname.."'...")
+      stats_inc("notrun")
+    end
+  end
+  
+end
+
+
+
+
+---------------------
+-- Import function --
+---------------------
+
+function import(name)
+  
+  do_assert(is_string(name), "lunit.import() expects a single string as argument")
+  
+  local user_env = getfenv(2)
+  
+  --------------------------------------------------
+  -- Installs a specific function in the user env --
+  --------------------------------------------------
+  
+  local function install(funcname)
+    user_env[funcname] = P[funcname]
+  end
+  
+  
+  ----------------------------------------------------------
+  -- Install functions matching a pattern in the user env --
+  ----------------------------------------------------------
+  
+  local function install_pattern(pattern)
+    for funcname, _ in pairs(P) do
+      if string.find(funcname, pattern) then
+        install(funcname)
+      end
+    end
+  end
+  
+  ------------------------------------------------------------
+  -- Installs assert() and all assert_xxx() in the user env --
+  ------------------------------------------------------------
+  
+  local function install_asserts()
+    install_pattern("^assert.*")
+  end
+  
+  -------------------------------------------
+  -- Installs all is_xxx() in the user env --
+  -------------------------------------------
+  
+  local function install_tests()
+    install_pattern("^is_.+")
+  end
+  
+  if name == "asserts" or name == "assertions" then
+    install_asserts()
+  elseif name == "tests" or name == "checks" then
+    install_tests()
+  elseif name == "all" then
+    install_asserts()
+    install_tests()
+    install("TestCase")
+  elseif string.find(name, "^assert.*") and P[name] then
+    install(name)
+  elseif string.find(name, "^is_.+") and P[name] then
+    install(name)
+  elseif name == "TestCase" then
+    install("TestCase")
+  else
+    error("luniit.import(): invalid function '"..name.."' to import", 2)
+  end
+end
+
+
+
+
+--------------------------------------------------
+-- Installs a private environment on the caller --
+--------------------------------------------------
+
+function setprivfenv()
+  local new_env = { }
+  local new_env_mt = { __index = getfenv(2) }
+  setmetatable(new_env, new_env_mt)
+  setfenv(2, new_env)
+end
+
+
+
+
+--------------------------------------------------
+-- Increments a counter in the statistics table --  
+--------------------------------------------------
+
+function stats_inc(varname, value)
+  orig_assert(is_table(stats))
+  orig_assert(is_string(varname))
+  orig_assert(is_nil(value) or is_number(value))
+  if not stats[varname] then return end
+  stats[varname] = stats[varname] + (value or 1)
+end
+
+
+
+
diff -urN --exclude _darcs lua-5.0.2/src/Makefile lua-safe/src/Makefile
--- lua-5.0.2/src/Makefile	2002-12-13 07:12:11.000000000 -0500
+++ lua-safe/src/Makefile	2005-04-25 12:58:37.000000000 -0400
@@ -59,6 +59,7 @@
 	lobject.h \
 	lopcodes.h \
 	lparser.h \
+	lsafe.h \
 	lstate.h \
 	lstring.h \
 	ltable.h \
diff -urN --exclude _darcs lua-5.0.2/src/lapi.c lua-safe/src/lapi.c
--- lua-5.0.2/src/lapi.c	2003-04-07 10:36:08.000000000 -0400
+++ lua-safe/src/lapi.c	2005-04-25 13:32:08.000000000 -0400
@@ -19,6 +19,7 @@
 #include "lgc.h"
 #include "lmem.h"
 #include "lobject.h"
+#include "lsafe.h"
 #include "lstate.h"
 #include "lstring.h"
 #include "ltable.h"
@@ -920,3 +921,48 @@
   return name;
 }
 
+
+LUA_API void lua_setsafelevel (lua_State *L, int level) {
+  lua_lock(L);
+  if (L->safelevel <= level)
+    L->safelevel = level;
+  else
+    luaG_runerror(L, "safety violation: unable to lower safety level");
+  lua_unlock(L);
+}
+
+
+LUA_API int lua_getsafelevel (lua_State *L) {
+  int ret;
+  lua_lock(L);
+  ret = L->safelevel;
+  lua_unlock(L);
+  return ret;
+}
+
+LUA_API void lua_settaint (lua_State *L, int idx, int taint) {
+  StkId t;
+  lua_lock(L);
+  t = luaA_index(L, idx);
+  if (taint) {
+    _SAFE_ALLOW_TAINT(L);
+    SAFE_TAINT(t);
+  } else {
+    _SAFE_ALLOW_UNTAINT(L);
+    SAFE_UNTAINT(t);
+  }
+  L->top -= 2;
+  luaA_pushobject(L, t);
+  lua_unlock(L);
+}
+
+
+LUA_API int lua_gettaint (lua_State *L, int idx) {
+  StkId t;
+  int ret;
+  lua_lock(L);
+  t = luaA_index(L, idx);
+  ret = SAFE_IS_TAINTED(t);
+  lua_unlock(L);
+  return ret;
+}
diff -urN --exclude _darcs lua-5.0.2/src/lib/Makefile lua-safe/src/lib/Makefile
--- lua-5.0.2/src/lib/Makefile	2003-03-28 07:49:56.000000000 -0500
+++ lua-safe/src/lib/Makefile	2005-04-25 13:05:43.000000000 -0400
@@ -6,8 +6,8 @@
 
 EXTRA_DEFS= $(POPEN) $(TMPNAM) $(DEGREES) $(LOADLIB)
 
-OBJS= lauxlib.o lbaselib.o ldblib.o liolib.o lmathlib.o ltablib.o lstrlib.o loadlib.o
-SRCS= lauxlib.c lbaselib.c ldblib.c liolib.c lmathlib.c ltablib.c lstrlib.c loadlib.c
+OBJS= lauxlib.o lbaselib.o ldblib.o liolib.o lmathlib.o ltablib.o lstrlib.o loadlib.o lsafelib.o
+SRCS= lauxlib.c lbaselib.c ldblib.c liolib.c lmathlib.c ltablib.c lstrlib.c loadlib.c lsafelib.c
 
 T= $(LIB)/liblualib.a
 
diff -urN --exclude _darcs lua-5.0.2/src/lib/lauxlib.c lua-safe/src/lib/lauxlib.c
--- lua-5.0.2/src/lib/lauxlib.c	2003-04-07 10:35:00.000000000 -0400
+++ lua-safe/src/lib/lauxlib.c	2005-04-25 13:17:18.000000000 -0400
@@ -21,6 +21,7 @@
 #include "lua.h"
 
 #include "lauxlib.h"
+#include "luasafe.h"
 
 
 /* number of prereserved references (for internal use) */
@@ -494,6 +495,7 @@
   }
   else {
     lua_pushfstring(L, "@%s", filename);
+    SAFE_CHECK_LOAD(L, 1);
     lf.f = fopen(filename, "r");
   }
   if (lf.f == NULL) return errfile(L, fnameindex);  /* unable to open file */
diff -urN --exclude _darcs lua-5.0.2/src/lib/lbaselib.c lua-safe/src/lib/lbaselib.c
--- lua-5.0.2/src/lib/lbaselib.c	2004-03-03 19:45:13.000000000 -0500
+++ lua-safe/src/lib/lbaselib.c	2005-05-04 16:42:06.000000000 -0400
@@ -17,6 +17,7 @@
 
 #include "lauxlib.h"
 #include "lualib.h"
+#include "luasafe.h"
 
 
 
@@ -199,6 +200,33 @@
 }
 
 
+static int luaB_setsafelevel (lua_State *L) {
+  luaL_checktype(L, 1, LUA_TNUMBER);
+  lua_setsafelevel(L, (int)lua_tonumber(L, 1));
+  return 0;
+}
+
+
+static int luaB_getsafelevel (lua_State *L) {
+  lua_pushnumber(L, lua_getsafelevel(L));
+  return 1;
+}
+
+static int luaB_taint (lua_State *L) {
+  lua_settaint(L, 1, 1); 
+  return 1;
+}
+
+static int luaB_untaint (lua_State *L) {
+  lua_settaint(L, 1, 0); 
+  return 1;
+}
+
+static int luaB_istainted (lua_State *L) {
+  lua_pushboolean(L, lua_gettaint(L, 1));
+  return 1;
+}
+
 static int luaB_type (lua_State *L) {
   luaL_checkany(L, 1);
   lua_pushstring(L, lua_typename(L, lua_type(L, 1)));
@@ -260,6 +288,7 @@
 
 static int luaB_loadstring (lua_State *L) {
   size_t l;
+  SAFE_CHECK_EVAL(L, 1);
   const char *s = luaL_checklstring(L, 1, &l);
   const char *chunkname = luaL_optstring(L, 2, s);
   return load_aux(L, luaL_loadbuffer(L, s, l, chunkname));
@@ -527,6 +556,11 @@
   {"xpcall", luaB_xpcall},
   {"collectgarbage", luaB_collectgarbage},
   {"gcinfo", luaB_gcinfo},
+  {"setsafelevel", luaB_setsafelevel},
+  {"getsafelevel", luaB_getsafelevel},
+  {"taint", luaB_taint},
+  {"untaint", luaB_untaint},
+  {"istainted", luaB_istainted},
   {"loadfile", luaB_loadfile},
   {"dofile", luaB_dofile},
   {"loadstring", luaB_loadstring},
diff -urN --exclude _darcs lua-5.0.2/src/lib/liolib.c lua-safe/src/lib/liolib.c
--- lua-5.0.2/src/lib/liolib.c	2003-10-01 09:16:49.000000000 -0400
+++ lua-safe/src/lib/liolib.c	2005-05-04 19:20:31.000000000 -0400
@@ -18,6 +18,7 @@
 
 #include "lauxlib.h"
 #include "lualib.h"
+#include "luasafe.h"
 
 
 
@@ -158,6 +159,7 @@
 
 
 static int io_close (lua_State *L) {
+  SAFE_CHECK_FILE_MOD(L);
   if (lua_isnone(L, 1) && lua_type(L, lua_upvalueindex(1)) == LUA_TTABLE) {
     lua_pushstring(L, IO_OUTPUT);
     lua_rawget(L, lua_upvalueindex(1));
@@ -189,6 +191,8 @@
 static int io_open (lua_State *L) {
   const char *filename = luaL_checkstring(L, 1);
   const char *mode = luaL_optstring(L, 2, "r");
+  SAFE_CHECK_FILE_DATA(L, 1);
+  SAFE_CHECK_FILE_DATA(L, 2);
   FILE **pf = newfile(L);
   *pf = fopen(filename, mode);
   return (*pf == NULL) ? pushresult(L, 0, filename) : 1;
@@ -202,6 +206,8 @@
 #else
   const char *filename = luaL_checkstring(L, 1);
   const char *mode = luaL_optstring(L, 2, "r");
+  SAFE_CHECK_FILE_DATA(L, 1);
+  SAFE_CHECK_FILE_DATA(L, 2);
   FILE **pf = newfile(L);
   *pf = popen(filename, mode);
   return (*pf == NULL) ? pushresult(L, 0, filename) : 1;
@@ -211,6 +217,7 @@
 
 static int io_tmpfile (lua_State *L) {
   FILE **pf = newfile(L);
+  SAFE_CHECK_FILE_MOD(L);
   *pf = tmpfile();
   return (*pf == NULL) ? pushresult(L, 0, NULL) : 1;
 }
@@ -363,6 +370,7 @@
   int nargs = lua_gettop(L) - 1;
   int success;
   int n;
+  int i;
   if (nargs == 0) {  /* no arguments? */
     success = read_line(L, f);
     n = first+1;  /* to return 1 result */
@@ -401,6 +409,8 @@
     lua_pop(L, 1);  /* remove last result */
     lua_pushnil(L);  /* push nil instead */
   }
+  for (i=n-first; i > 0; i--)
+    lua_settaint(L, -i, 1);
   return n - first;
 }
 
@@ -437,6 +447,7 @@
   int nargs = lua_gettop(L) - 1;
   int status = 1;
   for (; nargs--; arg++) {
+    SAFE_CHECK_FILE_DATA(L, arg);
     if (lua_type(L, arg) == LUA_TNUMBER) {
       /* optimization: could be done exactly as for strings */
       status = status &&
@@ -468,6 +479,7 @@
   FILE *f = tofile(L, 1);
   int op = luaL_findstring(luaL_optstring(L, 2, "cur"), modenames);
   long offset = luaL_optlong(L, 3, 0);
+  SAFE_CHECK_FILE_DATA(L, 3);
   luaL_argcheck(L, op != -1, 2, "invalid mode");
   op = fseek(f, offset, mode[op]);
   if (op)
@@ -480,11 +492,13 @@
 
 
 static int io_flush (lua_State *L) {
+  SAFE_CHECK_FILE_MOD(L);
   return pushresult(L, fflush(getiofile(L, IO_OUTPUT)) == 0, NULL);
 }
 
 
 static int f_flush (lua_State *L) {
+  SAFE_CHECK_FILE_MOD(L);
   return pushresult(L, fflush(tofile(L, 1)) == 0, NULL);
 }
 
@@ -537,6 +551,7 @@
 */
 
 static int io_execute (lua_State *L) {
+  SAFE_CHECK_EXECUTE(L, 1);
   lua_pushnumber(L, system(luaL_checkstring(L, 1)));
   return 1;
 }
@@ -544,6 +559,7 @@
 
 static int io_remove (lua_State *L) {
   const char *filename = luaL_checkstring(L, 1);
+  SAFE_CHECK_FILE_DATA(L, 1);
   return pushresult(L, remove(filename) == 0, filename);
 }
 
@@ -551,6 +567,8 @@
 static int io_rename (lua_State *L) {
   const char *fromname = luaL_checkstring(L, 1);
   const char *toname = luaL_checkstring(L, 2);
+  SAFE_CHECK_FILE_DATA(L, 1);
+  SAFE_CHECK_FILE_DATA(L, 2);
   return pushresult(L, rename(fromname, toname) == 0, fromname);
 }
 
@@ -561,6 +579,7 @@
   return 0;
 #else
   char buff[L_tmpnam];
+  SAFE_CHECK_FILE_MOD(L);
   if (tmpnam(buff) != buff)
     return luaL_error(L, "unable to generate a unique filename in `tmpname'");
   lua_pushstring(L, buff);
@@ -571,6 +590,7 @@
 
 static int io_getenv (lua_State *L) {
   lua_pushstring(L, getenv(luaL_checkstring(L, 1)));  /* if NULL push nil */
+  lua_settaint(L, -1, 1);
   return 1;
 }
 
@@ -713,6 +733,7 @@
 
 
 static int io_exit (lua_State *L) {
+  SAFE_CHECK_OS_EXIT(L);
   exit(luaL_optint(L, 1, EXIT_SUCCESS));
   return 0;  /* to avoid warnings */
 }
diff -urN --exclude _darcs lua-5.0.2/src/lib/lsafelib.c lua-safe/src/lib/lsafelib.c
--- lua-5.0.2/src/lib/lsafelib.c	1969-12-31 19:00:00.000000000 -0500
+++ lua-safe/src/lib/lsafelib.c	2005-06-02 19:42:01.000000000 -0400
@@ -0,0 +1,35 @@
+/*
+ * Safe check functions
+ * Copyright (c) 2005 Zachary P. Landau <kapheine@divineinvasion.net>
+ */
+
+#include <sys/stat.h>
+
+#define lsafelib_c
+
+#include "lua.h"
+
+#include "lauxlib.h"
+#include "luasafe.h"
+
+static int safe_check_world_writable(lua_State *L, int idx) {
+  struct stat st;
+
+  if (stat(lua_tostring(L, idx), &st) < 0)
+    luaL_error(L, "could not stat %s", lua_tostring(L, idx));
+  return ((st.st_mode & (S_IWOTH)) == (S_IWOTH));
+}
+
+LUALIB_API void safe_check_load (lua_State *L, int idx) {
+  if (lua_gettaint(L, idx)) { 
+    switch (lua_getsafelevel(L)) { 
+      case 3: 
+      case 2: 
+	/* XXX: Really should check if the DIRECTORY is world writable, not the file */
+        if (safe_check_world_writable(L, idx))
+          luaL_error(L, "safety violation: cannot load from world-writable file");
+      case 1: 
+        luaL_error(L, "safety violation: cannot load tainted filename"); 
+    } 
+  } 
+}
diff -urN --exclude _darcs lua-5.0.2/src/lobject.h lua-safe/src/lobject.h
--- lua-5.0.2/src/lobject.h	2003-03-18 07:50:04.000000000 -0500
+++ lua-safe/src/lobject.h	2005-04-25 12:35:17.000000000 -0400
@@ -63,6 +63,7 @@
 typedef struct lua_TObject {
   int tt;
   Value value;
+  int tainted;
 } TObject;
 
 
@@ -93,40 +94,45 @@
 
 /* Macros to set values */
 #define setnvalue(obj,x) \
-  { TObject *i_o=(obj); i_o->tt=LUA_TNUMBER; i_o->value.n=(x); }
+  { TObject *i_o=(obj); i_o->tt=LUA_TNUMBER; i_o->value.n=(x); i_o->tainted = 0; }
 
 #define chgnvalue(obj,x) \
 	check_exp(ttype(obj)==LUA_TNUMBER, (obj)->value.n=(x))
 
 #define setpvalue(obj,x) \
-  { TObject *i_o=(obj); i_o->tt=LUA_TLIGHTUSERDATA; i_o->value.p=(x); }
+  { TObject *i_o=(obj); i_o->tt=LUA_TLIGHTUSERDATA; i_o->value.p=(x); i_o->tainted = 0; }
 
 #define setbvalue(obj,x) \
-  { TObject *i_o=(obj); i_o->tt=LUA_TBOOLEAN; i_o->value.b=(x); }
+  { TObject *i_o=(obj); i_o->tt=LUA_TBOOLEAN; i_o->value.b=(x); i_o->tainted = 0; }
 
 #define setsvalue(obj,x) \
   { TObject *i_o=(obj); i_o->tt=LUA_TSTRING; \
     i_o->value.gc=cast(GCObject *, (x)); \
+    i_o->tainted = 0; \
     lua_assert(i_o->value.gc->gch.tt == LUA_TSTRING); }
 
 #define setuvalue(obj,x) \
   { TObject *i_o=(obj); i_o->tt=LUA_TUSERDATA; \
     i_o->value.gc=cast(GCObject *, (x)); \
+    i_o->tainted = 0; \
     lua_assert(i_o->value.gc->gch.tt == LUA_TUSERDATA); }
 
 #define setthvalue(obj,x) \
   { TObject *i_o=(obj); i_o->tt=LUA_TTHREAD; \
     i_o->value.gc=cast(GCObject *, (x)); \
+    i_o->tainted = 0; \
     lua_assert(i_o->value.gc->gch.tt == LUA_TTHREAD); }
 
 #define setclvalue(obj,x) \
   { TObject *i_o=(obj); i_o->tt=LUA_TFUNCTION; \
     i_o->value.gc=cast(GCObject *, (x)); \
+    i_o->tainted = 0; \
     lua_assert(i_o->value.gc->gch.tt == LUA_TFUNCTION); }
 
 #define sethvalue(obj,x) \
   { TObject *i_o=(obj); i_o->tt=LUA_TTABLE; \
     i_o->value.gc=cast(GCObject *, (x)); \
+    i_o->tainted = 0; \
     lua_assert(i_o->value.gc->gch.tt == LUA_TTABLE); }
 
 #define setnilvalue(obj) ((obj)->tt=LUA_TNIL)
@@ -143,7 +149,8 @@
 #define setobj(obj1,obj2) \
   { const TObject *o2=(obj2); TObject *o1=(obj1); \
     checkconsistency(o2); \
-    o1->tt=o2->tt; o1->value = o2->value; }
+    o1->tt=o2->tt; o1->value = o2->value; \
+    o1->tainted = o2->tainted; }
 
 
 /*
diff -urN --exclude _darcs lua-5.0.2/src/lsafe.h lua-safe/src/lsafe.h
--- lua-5.0.2/src/lsafe.h	1969-12-31 19:00:00.000000000 -0500
+++ lua-safe/src/lsafe.h	2005-06-02 19:42:32.000000000 -0400
@@ -0,0 +1,30 @@
+/*
+ * VM-level Safety check maros
+ * Copyright (c) 2005 Zachary P. Landau <kapheine@divineinvasion.net>
+ */
+
+#ifndef lsafe_h
+#define lsafe_h
+
+
+#include "lua.h"
+
+
+#define SAFE_TAINT(o)                ((o)->tainted = 1)
+#define SAFE_UNTAINT(o)              ((o)->tainted = 0)
+#define SAFE_IS_TAINTED(o)           ((o)->tainted == 1)
+#define SAFE_IS_TAINTED_TWO(o1, o2)  (SAFE_IS_TAINTED(o1) || SAFE_IS_TAINTED(o2))
+
+#define _SAFE_CHECK_ASSIGN(L, o) \
+    {if (SAFE_IS_TAINTED(o) && L->safelevel >= 3) \
+        luaG_runerror(L, "safety violation: cannot assign tainted value");}
+#define _SAFE_TAINT_ASSIGN(L, o) \
+    {if (L->safelevel >= 2) \
+        SAFE_TAINT(o);}
+#define _SAFE_ALLOW_TAINT(L) \
+    {if (L->safelevel >= 3) \
+        luaG_runerror(L, "safety violation: cannot taint values");}
+#define _SAFE_ALLOW_UNTAINT(L) \
+    {if (L->safelevel >= 2) \
+        luaG_runerror(L, "safety violation: cannot untaint values");}
+#endif
diff -urN --exclude _darcs lua-5.0.2/src/lstate.c lua-safe/src/lstate.c
--- lua-5.0.2/src/lstate.c	2003-04-03 08:35:34.000000000 -0500
+++ lua-safe/src/lstate.c	2005-04-25 12:35:17.000000000 -0400
@@ -137,6 +137,7 @@
   L->nCcalls = 0;
   L->base_ci = L->ci = NULL;
   L->errfunc = 0;
+  L->safelevel = 0;
   setnilvalue(gt(L));
 }
 
diff -urN --exclude _darcs lua-5.0.2/src/lstate.h lua-safe/src/lstate.h
--- lua-5.0.2/src/lstate.h	2003-02-27 06:52:30.000000000 -0500
+++ lua-safe/src/lstate.h	2005-04-25 12:35:17.000000000 -0400
@@ -152,6 +152,7 @@
   GCObject *gclist;
   struct lua_longjmp *errorJmp;  /* current error recover point */
   ptrdiff_t errfunc;  /* current error handling function (stack index) */
+  int safelevel;
 };
 
 
diff -urN --exclude _darcs lua-5.0.2/src/lua/lua.c lua-safe/src/lua/lua.c
--- lua-5.0.2/src/lua/lua.c	2003-04-03 08:34:42.000000000 -0500
+++ lua-safe/src/lua/lua.c	2005-05-04 16:20:37.000000000 -0400
@@ -16,6 +16,7 @@
 
 #include "lauxlib.h"
 #include "lualib.h"
+#include "luasafe.h"
 
 
 /*
@@ -104,6 +105,7 @@
   "  -e stat  execute string `stat'\n"
   "  -i       enter interactive mode after executing `script'\n"
   "  -l name  load and run library `name'\n"
+  "  -t num   set safety level to `num`\n"
   "  -v       show version information\n"
   "  --       stop handling options\n" ,
   progname);
@@ -352,6 +354,10 @@
             return 1;  /* stop if file fails */
           break;
         }
+        case 't': {
+          lua_setsafelevel(L, strtol(argv[++i], (char **)NULL, 10));
+          break;
+        }
         case 'c': {
           l_message(progname, "option `-c' is deprecated");
           break;
diff -urN --exclude _darcs lua-5.0.2/src/lvm.c lua-safe/src/lvm.c
--- lua-5.0.2/src/lvm.c	2004-03-03 19:50:13.000000000 -0500
+++ lua-safe/src/lvm.c	2005-05-04 16:49:14.000000000 -0400
@@ -22,6 +22,7 @@
 #include "lgc.h"
 #include "lobject.h"
 #include "lopcodes.h"
+#include "lsafe.h"
 #include "lstate.h"
 #include "lstring.h"
 #include "ltable.h"
@@ -311,8 +312,25 @@
   return !l_isfalse(L->top);
 }
 
+static int has_tainted (lua_State *L, int total, int last)
+{
+  int ret = 0;
+
+  do {
+    StkId top = L->base + last;
+    if (SAFE_IS_TAINTED(top)) {
+      ret = 1;
+      break;
+    }
+    total--;
+    last--;
+  } while (total > 0);
+
+  return ret;
+}
 
 void luaV_concat (lua_State *L, int total, int last) {
+  int do_taint = has_tainted(L, total, last);
   do {
     StkId top = L->base + last + 1;
     int n = 2;  /* number of elements handled in this pass (at least 2) */
@@ -338,6 +356,8 @@
         tl += l;
       }
       setsvalue2s(top-n, luaS_newlstr(L, buffer, tl));
+      if (do_taint)
+        SAFE_TAINT(top-n);
     }
     total -= n-1;  /* got `n' strings to create 1 new */
     last -= n-1;
@@ -440,10 +460,12 @@
       }
       case OP_LOADK: {
         setobj2s(ra, KBx(i));
+        _SAFE_TAINT_ASSIGN(L, ra);
         break;
       }
       case OP_LOADBOOL: {
         setbvalue(ra, GETARG_B(i));
+	_SAFE_TAINT_ASSIGN(L, ra);
         if (GETARG_C(i)) pc++;  /* skip next instruction (if C) */
         break;
       }
@@ -483,6 +505,7 @@
         break;
       }
       case OP_SETGLOBAL: {
+        _SAFE_CHECK_ASSIGN(L, ra);
         lua_assert(ttisstring(KBx(i)) && ttistable(&cl->g));
         luaV_settable(L, &cl->g, KBx(i), ra);
         break;
@@ -500,6 +523,7 @@
         int b = GETARG_B(i);
         b = fb2int(b);
         sethvalue(ra, luaH_new(L, b, GETARG_C(i)));
+	_SAFE_TAINT_ASSIGN(L, ra);
         luaC_checkGC(L);
         break;
       }
@@ -521,45 +545,60 @@
       case OP_ADD: {
         TObject *rb = RKB(i);
         TObject *rc = RKC(i);
+        int taint = SAFE_IS_TAINTED_TWO(rb, rc);
         if (ttisnumber(rb) && ttisnumber(rc)) {
           setnvalue(ra, nvalue(rb) + nvalue(rc));
         }
         else
           Arith(L, ra, rb, rc, TM_ADD);
+        if (taint)
+          SAFE_TAINT(ra);
         break;
       }
       case OP_SUB: {
         TObject *rb = RKB(i);
         TObject *rc = RKC(i);
+        int taint = SAFE_IS_TAINTED_TWO(rb, rc);
         if (ttisnumber(rb) && ttisnumber(rc)) {
           setnvalue(ra, nvalue(rb) - nvalue(rc));
         }
         else
           Arith(L, ra, rb, rc, TM_SUB);
+        if (taint)
+          SAFE_TAINT(ra);
         break;
       }
       case OP_MUL: {
         TObject *rb = RKB(i);
         TObject *rc = RKC(i);
+        int taint = SAFE_IS_TAINTED_TWO(rb, rc);
         if (ttisnumber(rb) && ttisnumber(rc)) {
           setnvalue(ra, nvalue(rb) * nvalue(rc));
         }
         else
           Arith(L, ra, rb, rc, TM_MUL);
+        if (taint)
+          SAFE_TAINT(ra);
         break;
       }
       case OP_DIV: {
         TObject *rb = RKB(i);
         TObject *rc = RKC(i);
+        int taint = SAFE_IS_TAINTED_TWO(rb, rc);
         if (ttisnumber(rb) && ttisnumber(rc)) {
           setnvalue(ra, nvalue(rb) / nvalue(rc));
         }
         else
           Arith(L, ra, rb, rc, TM_DIV);
+        if (taint)
+          SAFE_TAINT(ra);
         break;
       }
       case OP_POW: {
+        int taint = SAFE_IS_TAINTED_TWO(RKB(i), RKC(i));
         Arith(L, ra, RKB(i), RKC(i), TM_POW);
+        if (taint)
+          SAFE_TAINT(ra);
         break;
       }
       case OP_UNM: {
@@ -578,6 +617,8 @@
       case OP_NOT: {
         int res = l_isfalse(RB(i));  /* next assignment may change this value */
         setbvalue(ra, res);
+        if (SAFE_IS_TAINTED(RB(i)))
+          SAFE_TAINT(ra);
         break;
       }
       case OP_CONCAT: {
