Skip to content

Instantly share code, notes, and snippets.

@pygy
Last active October 12, 2021 08:01
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save pygy/8032764 to your computer and use it in GitHub Desktop.
Save pygy/8032764 to your computer and use it in GitHub Desktop.
lutn: Load a JSON-like Lua expression, securely. (see also: ltin)
-- Loads a single Lua expression as a JSON-like document, securely.
-- function expressions are prohibited, and the __index of the
-- strings metatable is temporarily stripped or replaced.
-- see also: ltin
-- usage:
-- lutn = require "lutn"
-- result = lutn(source_string [, environment = nil] [, string_metatable_index = nil])
local _load
if (_VERSION == "Lua 5.1") and not pcall(require, "jit") then
function _load (ld, _, _, env)
local fun, err
fun, err = loadstring (ld)
if fun and env then
setfenv (fun, env)
end
return fun, err
end
else
_load = load
end
local
function longstring (txt, i)
local s, f = txt:find("^(=*)%[.-%]%1%]", i)
return f and f + 1 or nil
end
local
function comment(txt, i)
if txt:sub(i,i) == "[" then
local j = longstring(txt, i +1)
if j then return j end
end
i = txt:find("\n", i)
return i and i + 1 or nil
end
local
function simplestring(txt, i, quote)
-- quote is either the simple or double quote.
i = txt:find("[^\\]"..quote, i)
if not i then
return nil
else
return i + 1
end
end
local
function hasfunc(txt)
local i = 1
while true do
i = txt:find("[\"'[f%-]", i or #txt + 1)
if i == nil then return false end
local char = txt:sub(i,i)
if char == "f" then
local start = txt:find("^function%W", i)
if start then
if start == 1 or txt:find("^[^%w_]", i - 1) then
return true
end
end
i = i + 1
elseif char == "-" and txt:byte(i+1) == 45 then
i = comment(txt, i + 2)
elseif char == "[" then
i = longstring(txt, i + 1)
else -- '"' or "'"
i = simplestring(txt, i, char)
end
end
end
local default_env = {}
local
function lutn(source, env, s_mt_i2)
if source:find"function" then
if hasfunc(source) then
return false, "function keyword found in lutn document"
end
end
env = env or default_env
local code, err = _load("return nil or " .. source, nil, nil, env)
if not code then
return false, err
end
-- strip the string metatable __index
local s_mt, s_mt_i
s_mt = getmetatable""
s_mt_i, s_mt.__index = s_mt.__index, s_mt_i2
code, err = pcall(code)
-- restore the string metatable
s_mt.__index = s_mt_i
return code, err
end
return lutn
-- The MIT License (MIT)
--
-- Copyright © 2013 Pierre-Yves Gérardy
--
-- 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.
lutn = require "lutn"
assert(not lutn[[{(function()end)()}]])
assert(not lutn[[("foo"):rep(5)]])
assert(lutn[=[{
[ [==[function]==] ]="function", [ [[function]] ]='function'
-- function
--[===[function]===]
}]=])
assert(lutn([[{
foo"", (""):bar()
}]], {foo=function()end}, {bar=function()end}))
print"ok"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment