Handling lfs.dir when access to directory is denied - lua

The lfs module allows reading a directory using the lfs.dir iterator. If the directory cannot be accessed (e.g. because permissions do not allow it), an error is raised.
I could check that directory can be read like this:
local status, err = pcall(lfs.dir, "mydir")
if not status then
print("error occured", err)
else
for file in lfs.dir("mydir") do
print("file:", file)
end
end
However, this means calling lfs.dir twice. Not sure how much overhead that is, but given that I am working on a tool that scans huge directory structures very quickly, I would very much like to avoid it. Another solution would be to wrap the loop in a function and pcall that function. However, I was wondering whether the pcall can be built in the iterator itself, like creating a custom iterator as a "safe" wrapper around lfs.dir() that would allow me to silently ignore directories that cannot be read. How can I do that? And how much overhead would that be?

Using pcall
You don't have to look at thee implementation details of LFS to use pcall: The Lua reference manual tells you that there will be exactly 4 values evaluated to iterate a generic for:
The loop starts by evaluating explist to produce four values: an iterator function, a state, an initial value for the control variable, and a closing value.
Thus you can use pcall for any generic for iterator as follows:
local status, iterator_or_err, state, control_var, closing_val = pcall(lfs.dir, "mydir")
if status then
for file in iterator_or_err, state, control_var, closing_val do
print("file:", file)
end
else
print("error occurred", iterator_or_err)
end
Perhaps slightly more elegant would be capturing the return values in a table:
local t = {pcall(lfs.dir, "mydir")}
if t[1] then
for file in table.unpack(t, 2) do
print("file:", file)
end
else
print("error occurred", t[2])
end
Downside: This creates a garbage table. The most elegant solution is to use xpcall:
Using xpcall
Whenever you have to handle vararg returns, xpcall is handy because Lua will call a handler you provide with the status & the vararg.
xpcall(lfs.dir, function(status, ...)
if status then
for file in ... do
print("file:", file)
end
else
print("error occurred", ...)
end
end, "mydir")

Yes, you can invoke lfs.dir only once:
local status, err_or_iter, ud, v3, v4 = pcall(lfs.dir, "mydir")
if not status then
print("error occured", err_or_iter)
else
for file in err_or_iter, ud, v3, v4 do
print("file:", file)
end
end

Related

How to secure replacment?

So I need to secure my code.
My code is obfuscated and im using load(tostring(resultServerr))() but someone can do load = print on top and it will print everything from my code..
I have to secure it to avoid replacement but i dont know how.
If you use Lua 5.2 or higher, you can provide your own sandboxed _ENV table in 4th argument of load. If you use Lua 5.1 or 5.0 you can use setfenv, which works almost the same way as new _ENV.
local func, err = load(unsafecode, nil, nil, {})
if not func then print(err) return end
func()
or
local func, err = load(unsafecode)
if not func then print(err) return end
setfenv(func, {})
func()
Here I use empty table {} to protect from using all globals, adding new ones and overwriting existing. If you want to provide some functions, just add them into this table, they won't be removed from _G if they change it inside the sandboxed code.
It depends on how and where are you using lua environment and what normal user is allowed to do with lua?
If only you controls startup process of the server or programm and the user has no access to startup process, it is possible to do sandboxing on init state as suggested Spar, or something like:
init.lua:
function prepare()
local load = _G.load
_G.load_extension = function()
local obfuscated = get_extension()
load(obfuscated)
end
end
Original load is stored as local variable and can't be overwritten for load_extension in following lua scripts/functions.
If normal user can modify init scripts or can load external libraries (.dll/.so), you're out of luck to properly secure your code.
If the user has access to resultServerr content, you're out of luck hiding it, as this variable can be passed to any other process/function, not only print or load.
Overwrite print = nil then it can't print.
You can create your own hidden print function if you still need that capability; just remember what you called it, so you can access it later.
hidden_print = print
print = nil
print('this')
stdin:1: attempt to call a nil value (global 'print')
stack traceback:
stdin:1: in main chunk
[C]: in ?
hidden_print('that')
that

Tarantool blocks for operations, if error occurs inside box.begin()...box.commit() block

If error occurs inside box.begin()...box.commit() block, Tarantool box blocks for operations untill you manually execute box.commit()
For example, I provide incorrect data without one of fields to my function:
replace = function(space, data)
local id = data[1][1]
box.begin()
my_module.delete(space, id)
local response = my_module.insert(space, data)
box.commit()
return response
end
Inside of my_module.insert() it will encount an error, so execution of replace() will be aborted before execution of box.commit().
Later, if i call replace() again with another data, it won`t be able to execute box.begin() because of previously open transaction.
pcall(my_module.insert()) wasn`t able to catch an error.
There's a handy function for it - box.atomic
I suggest the following implementation:
replace = function(space, data)
return box.atomic(function()
local id = data[1][1]
my_module.delete(space, id)
return my_module.insert(space, data)
end)
end
pcall(my_module.insert()) wasn't able to catch an error.
pcall catches all errors. The error isn't caught probably because it occurs earlier on the delete operation.
By the way, Tarantool has inline operations for replace:
https://www.tarantool.io/en/doc/latest/reference/reference_lua/box_space_index/#box-space-replace

Lua (require) invoke an not intended print of required file name

When require is called in testt.lua which is one of two files the return is movee and movee.lua.
movee are for the most part a class to be required, but should be able to accept to be called direct with parameter.
movee.lua
local lib = {} --this is class array
function lib.moveAround( ... )
for i,direction in ipairs(arg) do
print(direction)
end
end
function lib.hello()
print("Hello water jump")
end
lib.moveAround(...)
return lib
testt.la
local move = require("movee")
Expected result is not to call lib.moveAround or print of file name when require is called.
Your expectations are incorrect. Lua, and most scripting languages for that matter, does not recognize much of a distinction between including a module and executing the Lua file which provides that module. Every function statement is a statement whose execution creates a function object. Until those statements are executed, those functions don't exist. Same goes for your local lib = {}. And so on.
Now, if you want to make a distinction between when a user tries to require your script as a module and when a user tries to execute your script on the command line (or via just loadfile or similar), then I would suggest doing the following.
Check the number of arguments the script was given. If no arguments were given, then your script was probably required, so don't do the stuff you don't want to do when the user requires your script:
local nargs = select("#", ...)
if(nargs > 0) then
lib.moveAround(...)
end
Solved by replacing
lib.moveAround(...)
with
local argument = {...}
if argument[1] ~= "movee" and argument[2] ~= "movee" then
lib.moveAround(...)
end
require("movee")
will execute the code within movee.lua
lib.moveAround(...)
is part of that code. Hence if you require "movee" you call lib.moveAround
If the expected result is not to call it, remove that line from your code or don't require that file.

A Lua iterator that fails silently?

I have a very simple problem with a simple iterator.
Let's say that I design a function, files(), that iterates over all the files in a folder:
for file in files("/path/to/folder") do
print(file)
end
Now, this seems perfect, but there's a problem here: What if the the folder doesn't exist, or we don't have read permission to it?
How would we indicate such error?
One solution would be to have files() return nil, "no read permission" in this case. We'd then be able to wrap the call to files() inside assert():
for file in assert(files("/path/to/folder")) do
print(file)
end
This seemingly solves the problem. But this forces our users to always use assert(). What if the user doesn't care about errors? For this kind of users we'd want our files() to behave as if the folder is empty. But Lua --in case files() indicates error-- would try to call the returned nil and this will result in an error ("attempt to call a nil value").
So,
How can we design an iterator, files(), that would cater to both users that care about errors and users that don't?
If it's not possible, what alternative would you suggest?
First: Instead of returning nil + error message consider raising an error in the files function (using error). This way you can't forget the assert call, and you won't get the confusing "attempt to call a nil value" error.
You could pass an extra boolean parameter to files when you don't want to raise errors -- you should return an empty function (function() end) instead of calling error in this case.
A more general approach is the following:
-- an iterator that immediately stops a for loop
local function dummy_iter() end
-- catch errors and skip for loop in that case
function iterpcall( g, ... )
local ok, f, st, var = pcall( g, ... )
if ok then
return f, st, var
else
return dummy_iter
end
end
for file in iterpcall( files, "/path/to/folder" ) do
print( file )
for line in iterpcall( io.lines, file ) do -- works for other iterators as well
print( line )
end
end
The implementation of iterpcall above only handles errors raised in the iterator generator (files or io.lines), not in the iterator function (f) itself. You would have to wrap f in a closure with a pcall to do that.
There also question what you whant to do if you get error in the middle of iteration (e.g. access deny for subfolder with recurcive iteration). In this case assert does not help.
In this case I create 2 variant of iterators (inner and outer).
-- raise error
for file in files(...) do ... end
-- return error
files(...,function(file) ... end)
Or just create 2 different iterators.

sanitizing a Lua table input

Let's say I want a Lua table that will be provided from a third party, not totally reliable, from a file or other IO source.
I get the table as a string, like "{['valid'] = 10}" and I can load it as
externalTable = loadstring("return " .. txtTable)()
But this opens a breach to code injection, ie.: txtTable = os.execute('rm -rf /')
So I did this sanitizing function:
function safeLoadTable(txtTable)
txtTable = tostring(txtTable)
if (string.find(txtTable, "(", 1, true))
then return nil end
local _start = string.find(txtTable, "{", 1, true)
local _end = string.find(string.reverse(txtTable), "}", 1, true)
if (_start == nil or _end == nil)
then return nil end
txtTable = string.sub(txtTable, _start, #txtTable - _end + 1)
print("cropped to ", txtTable)
local pFunc = loadstring("return " .. txtTable)
if (pFunc) then
local _, aTable = pcall(pFunc)
return aTable
end
end
In the worst case it should return nil.
Can this be considered safe against a "regular bad-intentioned person" :)
You could run the unsafe code in a sandbox.
Here is how a simple sandbox could look in Lua 5.1 (error handling omitted for brevity):
local script = [[os.execute("rm -rf /")]]
local env = { print=print, table=table, string=string }
local f, err = loadstring(script)
if err then
-- handle syntax error
end
setfenv(f, env)
local status, err = pcall(f)
if not status then
-- handle runtime error
end
In Lua 5.2 you can load the script into it's own environment using the load function.
The result would be a runtime error returned from pcall:
attempt to index global 'os' (a nil value)
EDIT
As Lorenzo Donati pointed out in the comments this is not a complete solution to stop rogue scripts. It essentially allows you to white-list functions and tables that are approved for user scripts.
For more info about handling rogue scripts I would suggest this SO question:
Embedded Lua - timing out rogue scripts (e.g. infinite loop) - an example anyone?
I don't think it is safe. Try this:
print(safeLoadTable [[{ foo = (function() print"yahoo" end)() } ]])
EDIT
or this, for more fun:
print(safeLoadTable [[{ foo = (function() print(os.getenv "PATH") end)() } ]])
I won't suggest the alternative of replacing that os.getenv with os.execute, though. :-)
The problem is not easy to solve. Code injection avoidance is not at all simple in this case because you are executing a piece of Lua code when doing that loadstring. No simple string matching technique is really safe. The only secure way would be to implement a parser for a subset of the Lua table syntax and use that parser on the string.
BTW, even Lua team stripped off the bytecode verifier from Lua 5.2 since they discovered that it was amenable to attacks, and bytecode is a far simpler language than Lua source code.
I created sandbox.lua for exactly this purpose. It'll handle both insecure stuff as well as DOS-type attacks, assuming that your environment has access to the debug facility.
https://github.com/kikito/sandbox.lua
Note that for now it is Lua 5.1-compatible only.
Running in sandbox isn't safe, inspecting source code is not very simple. An idea: inspect bytecode!
Emmm, actually that's not very simple either, but here is a lazy implementation: http://codepad.org/mGqQ0Y8q

Resources