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 "lauxlib.h"
|
||||||
# include "lua.h"
|
# include "lua.h"
|
||||||
# include "lualib.h"
|
# include "lualib.h"
|
||||||
|
|
||||||
|
# include <QFileInfo>
|
||||||
|
# include <QTextCodec>
|
||||||
namespace chatterino::lua::api {
|
namespace chatterino::lua::api {
|
||||||
|
|
||||||
int c2_register_command(lua_State *L)
|
int c2_register_command(lua_State *L)
|
||||||
|
@ -135,9 +138,78 @@ int g_load(lua_State *L)
|
||||||
}
|
}
|
||||||
|
|
||||||
lua_call(L, countArgs, LUA_MULTRET);
|
lua_call(L, countArgs, LUA_MULTRET);
|
||||||
qCDebug(chatterinoLua) << "FDM " << lua_gettop(L);
|
|
||||||
|
|
||||||
return 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
|
} // namespace chatterino::lua::api
|
||||||
#endif
|
#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_send_msg(lua_State *L); // NOLINT(readability-identifier-naming)
|
||||||
int c2_system_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
|
} // namespace chatterino::lua::api
|
||||||
|
|
||||||
|
|
|
@ -72,6 +72,11 @@ public:
|
||||||
|
|
||||||
std::set<QString> listRegisteredCommands();
|
std::set<QString> listRegisteredCommands();
|
||||||
|
|
||||||
|
const QDir &loadDirectory() const
|
||||||
|
{
|
||||||
|
return this->loadDirectory_;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QDir loadDirectory_;
|
QDir loadDirectory_;
|
||||||
lua_State *state_;
|
lua_State *state_;
|
||||||
|
|
|
@ -165,13 +165,25 @@ void PluginController::openLibrariesFor(lua_State *L,
|
||||||
// possibly randomize this name at runtime to prevent some attacks?
|
// possibly randomize this name at runtime to prevent some attacks?
|
||||||
lua_setfield(L, LUA_REGISTRYINDEX, "real_load");
|
lua_setfield(L, LUA_REGISTRYINDEX, "real_load");
|
||||||
|
|
||||||
|
lua_getfield(L, gtable, "dofile");
|
||||||
|
lua_setfield(L, LUA_REGISTRYINDEX, "real_dofile");
|
||||||
|
|
||||||
// NOLINTNEXTLINE
|
// NOLINTNEXTLINE
|
||||||
static const luaL_Reg replacementFuncs[] = {
|
static const luaL_Reg replacementFuncs[] = {
|
||||||
{"load", lua::api::g_load},
|
{"load", lua::api::g_load},
|
||||||
|
|
||||||
|
// chatterino dofile is way more similar to require() than dofile()
|
||||||
|
{"execfile", lua::api::g_dofile},
|
||||||
{nullptr, nullptr},
|
{nullptr, nullptr},
|
||||||
};
|
};
|
||||||
luaL_setfuncs(L, replacementFuncs, 0);
|
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);
|
lua_pop(L, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue