[TestMethod] public void Enum() { using (var lua = new Lua()) { lua["a"] = TestEnum.A; lua.RegisterType(typeof(TestEnum)); Assert.AreEqual(typeof(TestEnum), lua.Eval("a").GetType()); Assert.AreEqual(TestEnum.A, lua.Eval("a")); Assert.AreEqual(TestEnum.A, lua.Eval("TestEnum.A")); Assert.AreEqual(TestEnum.B, lua.Eval("...", TestEnum.B)); Assert.AreEqual(TestEnum.C, lua.Eval <TestEnum>("TestEnum.C")); } }
[TestMethod] public void Metatable() { using (var lua = new Lua()) { lua["o"] = new object(); Assert.AreNotEqual("table", lua.Eval("type(getmetatable(o))")); } }
[TestMethod] public void ImportType() { using (var lua = new Lua()) { foreach (var assembly in new [] { "mscorlib", "System", "LuaInterface" }) { try { lua.Eval("load_assembly(...)", assembly); } catch (LuaScriptException) {} } Assert.IsNull(lua.Eval("import_type('System.Diagnostics.Process')")); Assert.IsNull(lua.Eval("import_type('System.IO.File')")); Assert.IsNull(lua.Eval("import_type('LuaInterface.Lua')")); } }
[TestMethod] public void NewReference() { using (var lua = new Lua()) { var types = ( from assembly in AppDomain.CurrentDomain.GetAssemblies() from type in SafeGetTypes(assembly) where !type.IsAbstract && typeof(LuaBase).IsAssignableFrom(type) select type) .ToArray(); Trace.WriteLine("Types: " + string.Join(", ", types.Select(t => t.ToString()).ToArray())); foreach (var type in types) { var method = type.GetMethod("NewReference", BindingFlags.Public | BindingFlags.Instance, null, Type.EmptyTypes, null); Assert.IsTrue(method != null && type == method.ReturnType, string.Format("{0} inherits from LuaBase but has no NewReference() method.", type.FullName)); } using (var one = lua.NewTable()) using (var two = lua.NewTable()) using (var one_newref = one.NewReference()) Assert.IsTrue(one == one_newref && one != two && one_newref != two); using (var one = lua.LoadString("-- noop")) using (var two = lua.LoadString("-- nop")) using (var one_newref = one.NewReference()) Assert.IsTrue(one == one_newref && one != two && one_newref != two); using (var one = lua.NewUserData(0, null)) using (var two = lua.NewUserData(0, null)) using (var one_newref = one.NewReference()) Assert.IsTrue(one == one_newref && one != two && one_newref != two); using (var one = lua.NewString("a")) using (var two = lua.NewString("b")) using (var one_newref = one.NewReference()) Assert.IsTrue(one == one_newref && one != two && one_newref != two); using (var f = lua.Eval <LuaFunction>("function() end")) using (var one = lua.NewThread(f)) using (var two = lua.NewThread(f)) using (var one_newref = one.NewReference()) Assert.IsTrue(one == one_newref && one != two && one_newref != two); if (types.Length != 5) { Assert.Inconclusive("Not all LuaBase implementations are covered by this test."); } } }
[TestMethod] public void LoadAssembly() { using (var lua = new Lua()) { foreach (var assembly in new [] { "mscorlib", "System", "LuaInterface" }) { try { lua.Eval("load_assembly(...)", assembly); Assert.Fail("Succeeded in loading assembly: " + assembly); } catch (LuaScriptException) {} } } }
[TestMethod] public void Bytecode() { using (var lua = new Lua(true)) { const string retval = "pwned"; Action <LuaFunction> verify = func => { Assert.IsNotNull(func); var rets = func.Call(); func.Dispose(); Assert.IsNotNull(rets); Assert.AreEqual(retval, rets[0]); }; var function = lua.Eval <LuaFunction>("function() return '" + retval + "' end"); var dump = function.Dump(); function.Dispose(); function = null; Assert.AreEqual((byte)Lua.BytecodePrefix, dump[0]); var dump_str = lua.NewString(dump); verify(lua.Eval <LuaFunction>("assert(loadstring(...))", dump_str)); lua.SecureLuaFunctions(); // --------------------------------- verify(lua.LoadBuffer(dump, "bytecode")); // the C# API has no restrictions; you should check before loading user input through it function = lua.Eval <LuaFunction>("loadstring(...)", dump_str); if (function != null) { verify(function); Assert.Fail("bytecode loaded and executed successfully"); } dump_str.Dispose(); } }
[TestMethod] public void LoadType() { using (var lua = new Lua()) { lua.LoadType(typeof(System.DateTime)); lua.DoString("DateTime = import_type 'System.DateTime'"); Assert.AreEqual(new DateTime(12), lua.Eval("DateTime(12)")); Assert.AreEqual((double)new DateTime[12].Length, lua.Eval("DateTime[12].Length")); Assert.AreEqual(new DateTime(12).Subtract(new DateTime(6)), lua.Eval("DateTime(12):Subtract(DateTime(6))")); Assert.IsNull(lua.Eval("import_type('System.TimeSpan')")); Assert.IsNull(lua.Eval("import_type('System.Diagnostics.Process')")); Assert.IsNull(lua.Eval("import_type('System.IO.File')")); Assert.IsNull(lua.Eval("import_type('LuaInterface.Lua')")); } }
[TestMethod] public void Reflection() { using (var lua = new Lua()) { // with access to the object returned by GetType() it is possible to use MethodInfo.Invoke on arbitrary methods // therefore, SecureLuaFunctions blacklists all types in the System.Reflection namespace // blacklisted types are converted to strings (ToString) and the string is pushed instead. lua["object"] = new object(); lua.DoString(@"t = object:GetType()"); string[] expressions = { "t.Assembly", "t.Module", "t:GetMethod('ToString')", "t:GetConstructors()", // this evaluates to the string "System.Reflection.ConstructorInfo[]" "t:GetMethod('ReferenceEquals')", // note that ReferenceEquals is a static method }; foreach (var expr in expressions) { var result = lua.Eval(expr); UAssert.IsInstanceOf <string>(result, expr); } } }
static void TestAllowed(Lua lua, string expr) { Assert.AreEqual(true, lua.Eval(expr)); }
[TestMethod] public void Bytecode() { using (var lua = new Lua(true)) { Assert.IsTrue(lua.BytecodeEnabled); const string retval = "pwned"; Action <LuaFunction> verify = func => { Assert.IsNotNull(func); var rets = func.Call(); func.Dispose(); Assert.IsNotNull(rets); Assert.AreEqual(retval, rets[0]); }; var function = lua.Eval <LuaFunction>("function() return '" + retval + "' end"); var dump = function.Dump(); function.Dispose(); function = null; Assert.AreEqual((byte)Lua.BytecodePrefix, dump[0]); var dump_str = lua.NewString(dump); verify(lua.Eval <LuaFunction>("assert(loadstring(...))", dump_str)); lua.SecureLuaFunctions(); // --------------------------------- Assert.IsFalse(lua.BytecodeEnabled); // SecureLuaFunctions() doesn't directly secure the C# API but it does disable Lua.BytecodeEnabled which affects everything try { lua.LoadBuffer(dump, "bytecode"); Assert.Fail(); } catch (LuaScriptException ex) { Assert.AreEqual("[string \"bytecode\"]:1: unexpected symbol near 'char(27)'", ex.Message); } // safest way to load trusted bytecode { LuaFunction func; using (lua.BytecodeRegion(true)) func = lua.LoadBuffer(dump, "bytecode"); verify(func); } // be very careful about this pattern because all code in verify() will be running in the unsafe context //using (lua.BytecodeRegion(true)) // verify(lua.LoadBuffer(dump, "bytecode")); // Lua script functions should be incapable of loading bytecode regardless of the state of BytecodeEnabled // if you need Lua functions that can load bytecode, save them in closures or other private locations before calling SecureLuaFunctions() for (int i = 0; i < 2; ++i) { function = lua.Eval <LuaFunction>("loadstring(...)", dump_str); if (function != null) { verify(function); Assert.Fail("bytecode loaded and executed successfully"); } lua.BytecodeEnabled = true; } dump_str.Dispose(); } }