diff --git a/client/init.lua b/client/init.lua index d3df826..40dc4ea 100644 --- a/client/init.lua +++ b/client/init.lua @@ -16,6 +16,9 @@ function buildat.dump(thing) if type(thing) == 'number' then return ''..thing end + if type(thing) == 'boolean' then + if thing then return "true" else return "false" end + end if thing == nil then return "nil" end @@ -30,7 +33,7 @@ function buildat.dump(thing) s = s.."}" return s end - return "(?)" + return type(thing) end function buildat.Logger(module) local logger = {} diff --git a/extensions/urho3d/init.lua b/extensions/urho3d/init.lua index 4127463..4699a15 100644 --- a/extensions/urho3d/init.lua +++ b/extensions/urho3d/init.lua @@ -6,7 +6,7 @@ local dump = buildat.dump local M = {safe = {}} -- Set every plain value in global environment to the sandbox --- ...it's maybe safe enough... +-- ...it's maybe safe enough... TODO: Not safe for k, v in pairs(_G) do if type(v) == 'number' or type(v) == 'string' then --log:info("Setting sandbox["..k.."] = "..buildat.dump(v)) @@ -47,6 +47,101 @@ for _, v in ipairs(safe_globals) do M.safe[v] = _G[v] end +-- ResourceCache + +-- Checks that this is not an absolute file path or anything funny +local allowed_name_pattern = '^[a-zA-Z0-9][a-zA-Z0-9/._ ]*$' +function M.check_safe_resource_name(orig_name) + local name = orig_name + if type(name) ~= "string" then + error("Unsafe resource name: "..dump(orig_name).." (not string)") + end + if string.match(name, '^/.*$') then + error("Unsafe resource name: "..dump(orig_name).." (absolute path)") + end + if not string.match(name, allowed_name_pattern) then + error("Unsafe resource name: "..dump(orig_name).." (unneeded chars)") + end + if string.match(name, '[.][.]') then + error("Unsafe resource name: "..dump(orig_name).." (contains ..)") + end + log:verbose("Safe resource name: "..orig_name.." -> "..name) + return name +end + +-- Basic tests +assert(pcall(function() + M.check_safe_resource_name("/etc/passwd") +end) == false) +assert(pcall(function() + M.check_safe_resource_name(" /etc/passwd") +end) == false) +assert(pcall(function() + M.check_safe_resource_name("\t /etc/passwd") +end) == false) +assert(pcall(function() + M.check_safe_resource_name("Models/Box.mdl") +end) == true) +assert(pcall(function() + M.check_safe_resource_name("Fonts/Anonymous Pro.ttf") +end) == true) +assert(pcall(function() + M.check_safe_resource_name("test1/pink_texture.png") +end) == true) +assert(pcall(function() + M.check_safe_resource_name(" Box.mdl ") +end) == false) +assert(pcall(function() + M.check_safe_resource_name("../../foo") +end) == false) +assert(pcall(function() + M.check_safe_resource_name("abc$de") +end) == false) + +local hack_resaved_files = {} + +-- Create temporary file with wanted file name to make Urho3D load it correctly +function M.resave_file(resource_name) + M.check_safe_resource_name(resource_name) + local path = __buildat_get_file_path(resource_name) + if path == nil then + return nil + end + local path2 = hack_resaved_files[path] + if path2 == nil then + path2 = __buildat_get_path("tmp").."/"..resource_name + dir2 = string.match(path2, '^(.*)/.+$') + if dir2 then + if not __buildat_mkdir(dir2) then + error("Failed to create directory: \""..dir2.."\"") + end + end + log:info("Temporary path: "..path2) + local src = io.open(path, "rb") + local dst = io.open(path2, "wb") + while true do + local buf = src:read(100000) + if buf == nil then break end + dst:write(buf) + end + src:close() + dst:close() + hack_resaved_files[path] = path2 + end + return path2 +end + +M.safe.cache = { + GetResource = function(self, resource_type, resource_name) + local path = M.resave_file(resource_name) + -- Note: path is unused + resource_name = M.check_safe_resource_name(resource_name) + return cache:GetResource(resource_type, resource_name) + end, +} + +-- SubscribeToEvent + local sandbox_function_name_to_global_function_name = {} local next_global_function_i = 1 diff --git a/src/client/app.cpp b/src/client/app.cpp index 296d600..557936e 100644 --- a/src/client/app.cpp +++ b/src/client/app.cpp @@ -4,6 +4,7 @@ #include "core/log.h" #include "client/config.h" #include "client/state.h" +#include "interface/fs.h" #include #include #pragma GCC diagnostic push @@ -45,18 +46,18 @@ struct CApp: public App, public u3d::Application engineParameters_["LogName"] = "client_Urho3D.log"; engineParameters_["FullScreen"] = false; engineParameters_["Headless"] = false; + // TODO: Proper paths - engineParameters_["ResourcePaths"] = - "/home/celeron55/softat/Urho3D/Bin/CoreData;" - "/home/celeron55/softat/Urho3D/Bin/Data"; + ss_ tmp_path = interface::getGlobalFilesystem()->get_absolute_path( + g_client_config.cache_path+"/tmp"); + engineParameters_["ResourcePaths"] = u3d::String()+ + "/home/celeron55/softat/Urho3D/Bin/CoreData;"+ + "/home/celeron55/softat/Urho3D/Bin/Data;"+ + tmp_path.c_str(); engineParameters_["AutoloadPaths"] = ""; // Set up on_update event (this runs every frame) SubscribeToEvent(u3d::E_UPDATE, HANDLER(CApp, on_update)); - - // TODO: Set up input events (Call stuff like - // call_global_if_exists(L, "__buildat_key_down", 1, 0);) - // ...or don't? They should be available in Urho3D API. } ~CApp() @@ -129,6 +130,7 @@ struct CApp: public App, public u3d::Application DEF_BUILDAT_FUNC(get_file_content) DEF_BUILDAT_FUNC(get_file_path) DEF_BUILDAT_FUNC(get_path) + DEF_BUILDAT_FUNC(mkdir) DEF_BUILDAT_FUNC(pcall) DEF_BUILDAT_FUNC(cereal_binary_input) DEF_BUILDAT_FUNC(cereal_binary_output) @@ -140,7 +142,6 @@ struct CApp: public App, public u3d::Application lua_tostring(L, -1)); lua_pop(L, 1); } - //m_script->ExecuteFile(u3d::String(init_lua_path.c_str())); } void on_update(u3d::StringHash eventType, u3d::VariantMap &eventData) @@ -276,6 +277,19 @@ struct CApp: public App, public u3d::Application return 0; } + // mkdir(path: string) + static int l_mkdir(lua_State *L) + { + ss_ path = lua_tocppstring(L, 1); + bool ok = interface::getGlobalFilesystem()->create_directories(path); + if(!ok) + log_w(MODULE, "Failed to create directory: \"%s\"", cs(path)); + else + log_v(MODULE, "Created directory: \"%s\"", cs(path)); + lua_pushboolean(L, ok); + return 1; + } + static int handle_error(lua_State *L) { lua_getglobal(L, "debug"); diff --git a/test/testmodules/test1/client_lua/init.lua b/test/testmodules/test1/client_lua/init.lua index 244cb5e..72b9358 100644 --- a/test/testmodules/test1/client_lua/init.lua +++ b/test/testmodules/test1/client_lua/init.lua @@ -77,6 +77,7 @@ buildat.sub_packet("test1:add_box", function(data) -- 3) First it needs a model. We could just load this binaray blob: object.model = u3d.cache:GetResource("Model", "Models/Box.mdl") + assert(object.model) -- TODO: let's not. Let's generate some geometry! --[[local cc = CustomGeometry() cc.SetNumGeometries(1) @@ -91,9 +92,11 @@ buildat.sub_packet("test1:add_box", function(data) -- We use this Diff.xml file to define that we want diffuse rendering. It -- doesn't make much sense to define it ourselves as it consists of quite many -- parameters: - object.material:SetTechnique(0, u3d.cache:GetResource("Technique", "Techniques/Diff.xml")) + object.material:SetTechnique(0, + u3d.cache:GetResource("Technique", "Techniques/Diff.xml")) -- And load the texture from a file: - object.material:SetTexture(u3d.TU_DIFFUSE, u3d.cache:GetResource("Texture2D", "Textures/LogoLarge.png")) + object.material:SetTexture(u3d.TU_DIFFUSE, + u3d.cache:GetResource("Texture2D", "test1/pink_texture.png")) -- -- Make a non-useful but nice reply packet and send it to the server diff --git a/todo.txt b/todo.txt index abc6de6..57295e7 100644 --- a/todo.txt +++ b/todo.txt @@ -12,12 +12,7 @@ Buildat TODO - Implement easy Lua scripting on the server (Luabind or not? The purpose is to allow recycling the same Lua code on the client and the server, which means that Lua will dictate the function/object design) -- Creating globals in Lua should cause an error unless done with a special - interface - Move self-contained library-like Lua functions from client/app.cpp to a reusable location so that they can be used from server-side Lua in the future - util/codestyle.sh: Add some CMakeLists.txt formatting (case, whitespace) -- Make two extensions for urho3d, one that loads into a table, and one that - uses the table one and puts it in the global namespace. Then add engine - support for automatically clearing them from the glboal namespace - some kind - of a global namespace wrapper table? +- Proper whitelisting sandbox for Urho3D's Lua API