BREAKING: Replace custom import() with normal Lua require(). (#5014)
* Use require() instead of a custom import() * Also search relative to the current file * Update documentation
This commit is contained in:
@@ -15,6 +15,7 @@
|
||||
# include <QFileInfo>
|
||||
# include <QLoggingCategory>
|
||||
# include <QTextCodec>
|
||||
# include <QUrl>
|
||||
|
||||
namespace {
|
||||
using namespace chatterino;
|
||||
@@ -282,69 +283,87 @@ int g_load(lua_State *L)
|
||||
# endif
|
||||
}
|
||||
|
||||
int g_import(lua_State *L)
|
||||
int loadfile(lua_State *L, const QString &str)
|
||||
{
|
||||
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);
|
||||
luaL_error(L, "it is not allowed to call import() without arguments");
|
||||
return 1;
|
||||
}
|
||||
|
||||
auto *pl = getApp()->plugins->getPluginByStatePtr(L);
|
||||
QString fname;
|
||||
if (!lua::pop(L, &fname))
|
||||
if (pl == nullptr)
|
||||
{
|
||||
lua_pushnil(L);
|
||||
luaL_error(L, "chatterino g_import: expected a string for a filename");
|
||||
return 1;
|
||||
return luaL_error(L, "loadfile: internal error: no plugin?");
|
||||
}
|
||||
auto dir = QUrl(pl->loadDirectory().canonicalPath() + "/");
|
||||
auto file = dir.resolved(fname);
|
||||
|
||||
qCDebug(chatterinoLua) << "plugin" << pl->id << "is trying to load" << file
|
||||
<< "(its dir is" << dir << ")";
|
||||
if (!dir.isParentOf(file))
|
||||
if (!dir.isParentOf(str))
|
||||
{
|
||||
lua_pushnil(L);
|
||||
luaL_error(L, "chatterino g_import: filename must be inside of the "
|
||||
"plugin directory");
|
||||
// XXX: This intentionally hides the resolved path to not leak it
|
||||
lua::push(
|
||||
L, QString("requested module is outside of the plugin directory"));
|
||||
return 1;
|
||||
}
|
||||
QFileInfo info(str);
|
||||
if (!info.exists())
|
||||
{
|
||||
lua::push(L, QString("no file '%1'").arg(str));
|
||||
return 1;
|
||||
}
|
||||
|
||||
auto path = file.path(QUrl::FullyDecoded);
|
||||
QFile qf(path);
|
||||
qf.open(QIODevice::ReadOnly);
|
||||
if (qf.size() > 10'000'000)
|
||||
auto temp = str.toStdString();
|
||||
const auto *filename = temp.c_str();
|
||||
|
||||
auto res = luaL_loadfilex(L, filename, "t");
|
||||
// Yoinked from checkload lib/lua/src/loadlib.c
|
||||
if (res == LUA_OK)
|
||||
{
|
||||
lua_pushnil(L);
|
||||
luaL_error(L, "chatterino g_import: size limit of 10MB exceeded, what "
|
||||
"the hell are you doing");
|
||||
lua_pushstring(L, filename);
|
||||
return 2;
|
||||
}
|
||||
|
||||
return luaL_error(L, "error loading module '%s' from file '%s':\n\t%s",
|
||||
lua_tostring(L, 1), filename, lua_tostring(L, -1));
|
||||
}
|
||||
|
||||
int searcherAbsolute(lua_State *L)
|
||||
{
|
||||
auto name = QString::fromUtf8(luaL_checkstring(L, 1));
|
||||
name = name.replace('.', QDir::separator());
|
||||
|
||||
QString filename;
|
||||
auto *pl = getApp()->plugins->getPluginByStatePtr(L);
|
||||
if (pl == nullptr)
|
||||
{
|
||||
return luaL_error(L, "searcherAbsolute: internal error: no plugin?");
|
||||
}
|
||||
|
||||
QFileInfo file(pl->loadDirectory().filePath(name + ".lua"));
|
||||
return loadfile(L, file.canonicalFilePath());
|
||||
}
|
||||
|
||||
int searcherRelative(lua_State *L)
|
||||
{
|
||||
lua_Debug dbg;
|
||||
lua_getstack(L, 1, &dbg);
|
||||
lua_getinfo(L, "S", &dbg);
|
||||
auto currentFile = QString::fromUtf8(dbg.source, dbg.srclen);
|
||||
if (currentFile.startsWith("@"))
|
||||
{
|
||||
currentFile = currentFile.mid(1);
|
||||
}
|
||||
if (currentFile == "=[C]" || currentFile == "")
|
||||
{
|
||||
lua::push(
|
||||
L,
|
||||
QString(
|
||||
"Unable to load relative to file:caller has no source file"));
|
||||
return 1;
|
||||
}
|
||||
|
||||
// validate utf-8 to block bytecode exploits
|
||||
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);
|
||||
luaL_error(L, "invalid utf-8 in import() target (%s) is not allowed",
|
||||
fname.toStdString().c_str());
|
||||
return 1;
|
||||
}
|
||||
auto parent = QFileInfo(currentFile).dir();
|
||||
|
||||
// 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);
|
||||
auto name = QString::fromUtf8(luaL_checkstring(L, 1));
|
||||
name = name.replace('.', QDir::separator());
|
||||
QString filename =
|
||||
parent.canonicalPath() + QDir::separator() + name + ".lua";
|
||||
|
||||
return lua_gettop(L);
|
||||
return loadfile(L, filename);
|
||||
}
|
||||
|
||||
int g_print(lua_State *L)
|
||||
|
||||
Reference in New Issue
Block a user