mirror of
https://github.com/Chatterino/chatterino2.git
synced 2024-11-21 22:24:07 +01:00
Replace Lua loadfile/dofile with execfile()
This commit is contained in:
parent
24c6003043
commit
e331e49310
4 changed files with 92 additions and 2 deletions
|
@ -13,6 +13,9 @@
|
|||
# include "lauxlib.h"
|
||||
# include "lua.h"
|
||||
# include "lualib.h"
|
||||
|
||||
# include <QFileInfo>
|
||||
# include <QTextCodec>
|
||||
namespace chatterino::lua::api {
|
||||
|
||||
int c2_register_command(lua_State *L)
|
||||
|
@ -135,9 +138,78 @@ int g_load(lua_State *L)
|
|||
}
|
||||
|
||||
lua_call(L, countArgs, LUA_MULTRET);
|
||||
qCDebug(chatterinoLua) << "FDM " << lua_gettop(L);
|
||||
|
||||
return lua_gettop(L);
|
||||
}
|
||||
|
||||
int g_dofile(lua_State *L)
|
||||
{
|
||||
auto countArgs = lua_gettop(L);
|
||||
// Lua allows dofile() which loads from stdin, but this is very useless in our case
|
||||
if (countArgs == 0)
|
||||
{
|
||||
lua_pushnil(L);
|
||||
// NOLINTNEXTLINE
|
||||
luaL_error(L, "it is not allowed to call dofile() without arguments");
|
||||
return 1;
|
||||
}
|
||||
|
||||
auto *pl = getApp()->plugins->getPluginByStatePtr(L);
|
||||
QString fname;
|
||||
if (!lua::pop(L, &fname))
|
||||
{
|
||||
lua_pushnil(L);
|
||||
// NOLINTNEXTLINE
|
||||
luaL_error(L, "chatterino g_dofile: expected a string for a filename");
|
||||
return 1;
|
||||
}
|
||||
auto dir = QUrl(pl->loadDirectory().canonicalPath() + "/");
|
||||
auto file = dir.resolved(fname);
|
||||
|
||||
qCDebug(chatterinoLua) << "plugin" << pl->codename << "is trying to load"
|
||||
<< file << "(its dir is" << dir << ")";
|
||||
if (!dir.isParentOf(file))
|
||||
{
|
||||
lua_pushnil(L);
|
||||
// NOLINTNEXTLINE
|
||||
luaL_error(L, "chatterino g_dofile: filename must be inside of the "
|
||||
"plugin directory");
|
||||
return 1;
|
||||
}
|
||||
|
||||
auto path = file.path(QUrl::FullyDecoded);
|
||||
// validate utf-8 to block bytecode exploits
|
||||
QFile qf(path);
|
||||
qf.open(QIODevice::ReadOnly);
|
||||
if (qf.size() > 10'000'000)
|
||||
{
|
||||
lua_pushnil(L);
|
||||
// NOLINTNEXTLINE
|
||||
luaL_error(L, "chatterino g_dofile: size limit of 10MB exceeded, what "
|
||||
"the hell are you doing");
|
||||
return 1;
|
||||
}
|
||||
auto data = qf.readAll();
|
||||
auto *utf8 = QTextCodec::codecForName("UTF-8");
|
||||
QTextCodec::ConverterState state;
|
||||
utf8->toUnicode(data.constData(), data.size(), &state);
|
||||
if (state.invalidChars != 0)
|
||||
{
|
||||
lua_pushnil(L);
|
||||
// NOLINTNEXTLINE
|
||||
luaL_error(L, "invalid utf-8 in dofile() target (%s) is not allowed",
|
||||
fname.toStdString().c_str());
|
||||
return 1;
|
||||
}
|
||||
|
||||
// fetch dofile and call it
|
||||
lua_getfield(L, LUA_REGISTRYINDEX, "real_dofile");
|
||||
// maybe data race here if symlink was swapped?
|
||||
lua::push(L, path);
|
||||
lua_call(L, 1, LUA_MULTRET);
|
||||
|
||||
return lua_gettop(L);
|
||||
}
|
||||
|
||||
} // namespace chatterino::lua::api
|
||||
#endif
|
||||
|
|
|
@ -9,7 +9,8 @@ int c2_register_command(lua_State *L); // NOLINT(readability-identifier-naming)
|
|||
int c2_send_msg(lua_State *L); // NOLINT(readability-identifier-naming)
|
||||
int c2_system_msg(lua_State *L); // NOLINT(readability-identifier-naming)
|
||||
|
||||
int g_load(lua_State *L); // NOLINT(readability-identifier-naming)
|
||||
int g_load(lua_State *L); // NOLINT(readability-identifier-naming)
|
||||
int g_dofile(lua_State *L); // NOLINT(readability-identifier-naming)
|
||||
|
||||
} // namespace chatterino::lua::api
|
||||
|
||||
|
|
|
@ -72,6 +72,11 @@ public:
|
|||
|
||||
std::set<QString> listRegisteredCommands();
|
||||
|
||||
const QDir &loadDirectory() const
|
||||
{
|
||||
return this->loadDirectory_;
|
||||
}
|
||||
|
||||
private:
|
||||
QDir loadDirectory_;
|
||||
lua_State *state_;
|
||||
|
|
|
@ -165,13 +165,25 @@ void PluginController::openLibrariesFor(lua_State *L,
|
|||
// possibly randomize this name at runtime to prevent some attacks?
|
||||
lua_setfield(L, LUA_REGISTRYINDEX, "real_load");
|
||||
|
||||
lua_getfield(L, gtable, "dofile");
|
||||
lua_setfield(L, LUA_REGISTRYINDEX, "real_dofile");
|
||||
|
||||
// NOLINTNEXTLINE
|
||||
static const luaL_Reg replacementFuncs[] = {
|
||||
{"load", lua::api::g_load},
|
||||
|
||||
// chatterino dofile is way more similar to require() than dofile()
|
||||
{"execfile", lua::api::g_dofile},
|
||||
{nullptr, nullptr},
|
||||
};
|
||||
luaL_setfuncs(L, replacementFuncs, 0);
|
||||
|
||||
lua_pushnil(L);
|
||||
lua_setfield(L, gtable, "loadfile");
|
||||
|
||||
lua_pushnil(L);
|
||||
lua_setfield(L, gtable, "dofile");
|
||||
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue