Skip to content
Snippets Groups Projects
Commit 94d527e1 authored by George Kiagiadakis's avatar George Kiagiadakis
Browse files

wplua: add flags to modify the sandbox behavior

with ISOLATE_ENV, it isolates the global environment between scripts
just like it did before; without it, it uses a common environment

with MINIMAL_STD, it restricts even further the available library
functions; useful for configuration files that don't need to do
actual scripting, just to define some tables
parent cb228637
No related branches found
No related tags found
No related merge requests found
......@@ -8,30 +8,9 @@
--
-- SPDX-License-Identifier: MIT
SANDBOX_ENV_LIST = {}
local SANDBOX_ENV = {}
-- List of safe functions and packages
([[
_VERSION assert error ipairs next pairs
pcall select tonumber tostring type xpcall
table utf8
math.abs math.acos math.asin math.atan math.ceil
math.cos math.deg math.exp math.tointeger math.floor math.fmod
math.huge math.ult math.log math.maxinteger math.mininteger math.max
math.min math.modf math.pi math.rad math.random
math.sin math.sqrt math.tan math.type
string.byte string.char string.find string.format string.gmatch
string.gsub string.len string.lower string.match string.reverse
string.sub string.upper
os.clock os.difftime os.time os.date os.getenv
]]):gsub('%S+', function(id)
local function populate_env(id)
local module, method = id:match('([^%.]+)%.([^%.]+)')
if module then
SANDBOX_ENV[module] = SANDBOX_ENV[module] or {}
......@@ -39,7 +18,43 @@ local SANDBOX_ENV = {}
else
SANDBOX_ENV[id] = _G[id]
end
end)
end
-- List of safe functions and packages
if SANDBOX_CONFIG["minimal_std"] then
-- minimal list, used for config files
([[
_VERSION ipairs pairs select tonumber tostring type
table
string.byte string.char string.find string.format string.gmatch
string.gsub string.len string.lower string.match string.reverse
string.sub string.upper
]]):gsub('%S+', populate_env)
else
-- full list, used for scripts
([[
_VERSION assert error ipairs next pairs
pcall select tonumber tostring type xpcall
table utf8
math.abs math.acos math.asin math.atan math.ceil
math.cos math.deg math.exp math.tointeger math.floor math.fmod
math.huge math.ult math.log math.maxinteger math.mininteger math.max
math.min math.modf math.pi math.rad math.random
math.sin math.sqrt math.tan math.type
string.byte string.char string.find string.format string.gmatch
string.gsub string.len string.lower string.match string.reverse
string.sub string.upper
os.clock os.difftime os.time os.date os.getenv
]]):gsub('%S+', populate_env)
end
-- Additionally export everything in SANDBOX_EXPORT
if type(SANDBOX_EXPORT) == "table" then
......@@ -60,16 +75,36 @@ for k, v in pairs(SANDBOX_ENV) do
end
end
function sandbox(chunk)
if SANDBOX_CONFIG["isolate_env"] then
-- in isolate_env mode, use a separate enviornment for each loaded chunk and
-- store all of them in a global table so that they are not garbage collected
SANDBOX_ENV_LIST = {}
function sandbox(chunk)
-- chunk's environment will be an empty table with __index
-- to access our SANDBOX_ENV (without being able to write it)
local env = setmetatable({}, {
__index = SANDBOX_ENV,
})
-- store the chunk's environment so that it is not garbage collected
table.insert(SANDBOX_ENV_LIST, env)
-- set it as the chunk's 1st upvalue (__ENV)
debug.setupvalue(chunk, 1, env)
-- execute the chunk
chunk()
end
else
-- in common_env mode, use the same environment for all loaded chunks
-- chunk's environment will be an empty table with __index
-- to access our SANDBOX_ENV (without being able to write it)
local env = setmetatable({}, {
SANDBOX_COMMON_ENV = setmetatable({}, {
__index = SANDBOX_ENV,
})
-- set it as the chunk's 1st upvalue (__ENV)
debug.setupvalue(chunk, 1, env)
-- store the chunk's environment so that it is not garbage collected
table.insert(SANDBOX_ENV_LIST, env)
-- execute the chunk
chunk()
function sandbox(chunk)
-- set it as the chunk's 1st upvalue (__ENV)
debug.setupvalue(chunk, 1, SANDBOX_COMMON_ENV)
-- execute the chunk
chunk()
end
end
......@@ -115,10 +115,20 @@ wplua_free (lua_State * L)
}
void
wplua_enable_sandbox (lua_State * L)
wplua_enable_sandbox (lua_State * L, WpLuaSandboxFlags flags)
{
g_autoptr (GError) error = NULL;
wp_debug ("enabling Lua sandbox");
lua_newtable (L);
lua_pushliteral (L, "minimal_std");
lua_pushboolean (L, (flags & WP_LUA_SANDBOX_MINIMAL_STD));
lua_settable (L, -3);
lua_pushliteral (L, "isolate_env");
lua_pushboolean (L, (flags & WP_LUA_SANDBOX_ISOLATE_ENV));
lua_settable (L, -3);
lua_setglobal (L, "SANDBOX_CONFIG");
if (!wplua_load_uri (L, URI_SANDBOX, &error)) {
wp_critical ("Failed to load sandbox: %s", error->message);
}
......
......@@ -39,11 +39,15 @@ typedef enum {
WP_LUA_ERROR_RUNTIME,
} WpLuaError;
typedef enum {
WP_LUA_SANDBOX_MINIMAL_STD,
WP_LUA_SANDBOX_ISOLATE_ENV,
} WpLuaSandboxFlags;
lua_State * wplua_new (void);
void wplua_free (lua_State * L);
void wplua_enable_sandbox (lua_State * L);
void wplua_enable_sandbox (lua_State * L, WpLuaSandboxFlags flags);
void wplua_register_type_methods (lua_State * L, GType type,
lua_CFunction constructor, const luaL_Reg * methods);
......
......@@ -404,7 +404,7 @@ test_wplua_signals ()
}
static void
test_wplua_sandbox ()
test_wplua_sandbox_script ()
{
g_autoptr (GError) error = NULL;
lua_State *L = wplua_new ();
......@@ -420,7 +420,7 @@ test_wplua_sandbox ()
wplua_load_buffer (L, code, sizeof (code) - 1, &error);
g_assert_no_error (error);
wplua_enable_sandbox (L);
wplua_enable_sandbox (L, WP_LUA_SANDBOX_ISOLATE_ENV);
const gchar code2[] =
"o = TestObject_new()\n";
......@@ -464,6 +464,44 @@ test_wplua_sandbox ()
wplua_free (L);
}
static void
test_wplua_sandbox_config ()
{
g_autoptr (GError) error = NULL;
lua_State *L = wplua_new ();
wplua_enable_sandbox (L, WP_LUA_SANDBOX_MINIMAL_STD);
const gchar code3[] =
"o = { answer = 42 }\n";
wplua_load_buffer (L, code3, sizeof (code3) - 1, &error);
g_assert_no_error (error);
/* no assert() in minimal_std mode, resort to other means of failure */
const gchar code4[] =
"if (o.answer ~= 42) then\n"
" non_existent_function()\n"
"end\n";
wplua_load_buffer (L, code4, sizeof (code4) - 1, &error);
g_assert_no_error (error);
/* string.* is protected */
const gchar code6[] =
"string.test = 'hello world'\n";
wplua_load_buffer (L, code6, sizeof (code6) - 1, &error);
g_debug ("expected error: %s", error ? error->message : "null");
g_assert_error (error, WP_DOMAIN_LUA, WP_LUA_ERROR_RUNTIME);
g_clear_error (&error);
/* this would be an error if the assert function was exported, but it's not */
const gchar code7[] =
"assert = 'hello world'\n";
wplua_load_buffer (L, code7, sizeof (code7) - 1, &error);
g_assert_no_error (error);
wplua_free (L);
}
gint
main (gint argc, gchar *argv[])
{
......@@ -475,7 +513,8 @@ main (gint argc, gchar *argv[])
g_test_add_func ("/wplua/properties", test_wplua_properties);
g_test_add_func ("/wplua/closure", test_wplua_closure);
g_test_add_func ("/wplua/signals", test_wplua_signals);
g_test_add_func ("/wplua/sandbox", test_wplua_sandbox);
g_test_add_func ("/wplua/sandbox/script", test_wplua_sandbox_script);
g_test_add_func ("/wplua/sandbox/config", test_wplua_sandbox_config);
return g_test_run ();
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment