diff --git a/launcher/modplatform/atlauncher/ATLPackInstallTask.cpp b/launcher/modplatform/atlauncher/ATLPackInstallTask.cpp index 7a7365fbc..96e577cb2 100644 --- a/launcher/modplatform/atlauncher/ATLPackInstallTask.cpp +++ b/launcher/modplatform/atlauncher/ATLPackInstallTask.cpp @@ -59,6 +59,16 @@ #include "BuildConfig.h" #include "ui/dialogs/BlockedModsDialog.h" +namespace { +bool isPathTraversal(const QString& basePath, const QString& entryName) +{ + auto safeName = FS::RemoveInvalidPathChars(entryName); + auto fullPath = FS::PathCombine(basePath, safeName); + auto baseUrl = QUrl::fromLocalFile(basePath); + return !baseUrl.isParentOf(QUrl::fromLocalFile(fullPath)); +} +} // namespace + namespace ATLauncher { static Meta::Version::Ptr getComponentVersion(const QString& uid, const QString& version); @@ -938,6 +948,10 @@ bool PackInstallTask::extractMods(const QMap& toExtract, folderToExtract = mod.extractFolder; static const QRegularExpression s_regex("^/"); folderToExtract.remove(s_regex); + if (isPathTraversal(extractToPath, folderToExtract)) { + qWarning() << "Blocked path traversal in" << mod.extractFolder; + return false; + } } qDebug() << "Extracting " + mod.file + " to " + extractToDir; @@ -955,6 +969,11 @@ bool PackInstallTask::extractMods(const QMap& toExtract, QDir extractDir(m_stagingPath); auto extractToPath = FS::PathCombine(extractDir.absolutePath(), "minecraft", extractToDir, mod.decompFile); + if (isPathTraversal(extractToPath, mod.decompFile)) { + qWarning() << "Blocked path traversal in decompFile" << mod.decompFile; + return false; + } + qDebug() << "Extracting " + mod.decompFile + " to " + extractToDir; if (!MMCZip::extractFile(modPath, mod.decompFile, extractToPath)) { qWarning() << "Failed to extract" << mod.decompFile;