Commit c290d489 authored by Christoph Cullmann's avatar Christoph Cullmann 🐮
Browse files

start to keep some allowed/block lists

parent 508648e1
......@@ -40,6 +40,8 @@ static const QString CONFIG_SERVER_CONFIG{QStringLiteral("ServerConfiguration")}
static const QString CONFIG_SEMANTIC_HIGHLIGHTING{QStringLiteral("SemanticHighlighting")};
static const QString CONFIG_SIGNATURE_HELP{QStringLiteral("SignatureHelp")};
static const QString CONFIG_AUTO_IMPORT{QStringLiteral("AutoImport")};
static const QString CONFIG_ALLOWED_COMMANDS{QStringLiteral("AllowedServerCommandLines")};
static const QString CONFIG_BLOCKED_COMMANDS{QStringLiteral("BlockedServerCommandLines")};
K_PLUGIN_FACTORY_WITH_JSON(LSPClientPluginFactory, "lspclientplugin.json", registerPlugin<LSPClientPlugin>();)
......@@ -113,6 +115,17 @@ void LSPClientPlugin::readConfig()
m_signatureHelp = config.readEntry(CONFIG_SIGNATURE_HELP, true);
m_autoImport = config.readEntry(CONFIG_AUTO_IMPORT, true);
// read allow + block lists as two separate keys, let block always win
const auto allowed = config.readEntry(CONFIG_ALLOWED_COMMANDS, QStringList());
const auto blocked = config.readEntry(CONFIG_BLOCKED_COMMANDS, QStringList());
m_serverCommandLineToAllowedState.clear();
for (const auto &cmd : allowed) {
m_serverCommandLineToAllowedState[cmd] = true;
}
for (const auto &cmd : blocked) {
m_serverCommandLineToAllowedState[cmd] = false;
}
Q_EMIT update();
}
......@@ -141,6 +154,18 @@ void LSPClientPlugin::writeConfig() const
config.writeEntry(CONFIG_SIGNATURE_HELP, m_signatureHelp);
config.writeEntry(CONFIG_AUTO_IMPORT, m_autoImport);
// write allow + block lists as two separate keys
QStringList allowed, blocked;
for (const auto &it : m_serverCommandLineToAllowedState) {
if (it.second) {
allowed.push_back(it.first);
} else {
blocked.push_back(it.first);
}
}
config.writeEntry(CONFIG_ALLOWED_COMMANDS, allowed);
config.writeEntry(CONFIG_BLOCKED_COMMANDS, blocked);
Q_EMIT update();
}
......
......@@ -61,6 +61,9 @@ public:
// debug mode?
bool m_debugMode = false;
// hash of allowed and blacklisted server command lines
std::map<QString, bool> m_serverCommandLineToAllowedState;
// get current config path
QUrl configPath() const
{
......
......@@ -19,6 +19,7 @@
#include <KTextEditor/MovingInterface>
#include <KTextEditor/View>
#include <QCheckBox>
#include <QDir>
#include <QEventLoop>
#include <QFileInfo>
......@@ -26,6 +27,7 @@
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonParseError>
#include <QMessageBox>
#include <QRegularExpression>
#include <QStandardPaths>
#include <QThread>
......@@ -734,33 +736,75 @@ private:
// use full path to avoid security issues
cmdline[0] = cmd;
// an empty list is always passed here (or null)
// the initial list is provided/updated using notification after start
// since that is what a server is more aware of
// and should support if it declares workspace folder capable
// (as opposed to the new initialization property)
LSPClientServer::FoldersType folders;
if (useWorkspace) {
folders = QList<LSPWorkspaceFolder>();
// check our allow list
const QString fullCommandLineString = cmdline.join(QStringLiteral(" "));
const auto it = m_plugin->m_serverCommandLineToAllowedState.find(fullCommandLineString);
bool startServerAllowed = false;
bool remembered = false;
if (it == m_plugin->m_serverCommandLineToAllowedState.end()) {
// ask user if the start should be allowed, allow to have this remembered to be permanent pain
QMessageBox msgBox;
msgBox.setText(i18n("LSP server start requested"));
msgBox.setInformativeText(i18n("Do you want the LSP server to be started? Full command line is '%1'", fullCommandLineString));
msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
msgBox.setDefaultButton(QMessageBox::Yes);
msgBox.setCheckBox(new QCheckBox(i18n("Remember this for this command line."), &msgBox));
msgBox.checkBox()->setChecked(true);
// are we allowed?
startServerAllowed = (msgBox.exec() == QMessageBox::Yes);
// remember the state if wanted, ensure we store config directly to not loose this
if (msgBox.checkBox()->isChecked()) {
remembered = true;
m_plugin->m_serverCommandLineToAllowedState.emplace(fullCommandLineString, startServerAllowed);
m_plugin->writeConfig();
}
} else {
// use already stored result
remembered = true;
startServerAllowed = it->second;
}
server.reset(new LSPClientServer(cmdline, root, realLangId, serverConfig.value(QStringLiteral("initializationOptions")), folders));
connect(server.data(), &LSPClientServer::stateChanged, this, &self_type::onStateChanged, Qt::UniqueConnection);
if (!server->start()) {
QString message = i18n("Failed to start server: %1", cmdline.join(QLatin1Char(' ')));
const auto url = serverConfig.value(QStringLiteral("url")).toString();
if (!url.isEmpty()) {
message += QStringLiteral("\n") + i18n("Please check your PATH for the binary");
message += QStringLiteral("\n") + i18n("See also %1 for installation or details", url);
// abort start with proper log message if not allowed
if (!startServerAllowed) {
QString message;
if (remembered) {
message =
i18n("User permanently blocked start of: '%1'.\nUse the config page of the plugin to undo this block.", fullCommandLineString);
} else {
message = i18n("User blocked start of: '%1'", fullCommandLineString);
}
showMessage(message, KTextEditor::Message::Warning);
showMessage(message, KTextEditor::Message::Information);
} else {
showMessage(i18n("Started server %2: %1", cmdline.join(QLatin1Char(' ')), serverDescription(server.data())),
KTextEditor::Message::Positive);
using namespace std::placeholders;
server->connect(server.data(), &LSPClientServer::logMessage, this, std::bind(&self_type::onMessage, this, true, _1));
server->connect(server.data(), &LSPClientServer::showMessage, this, std::bind(&self_type::onMessage, this, false, _1));
server->connect(server.data(), &LSPClientServer::workDoneProgress, this, &self_type::onWorkDoneProgress);
server->connect(server.data(), &LSPClientServer::workspaceFolders, this, &self_type::onWorkspaceFolders, Qt::UniqueConnection);
// an empty list is always passed here (or null)
// the initial list is provided/updated using notification after start
// since that is what a server is more aware of
// and should support if it declares workspace folder capable
// (as opposed to the new initialization property)
LSPClientServer::FoldersType folders;
if (useWorkspace) {
folders = QList<LSPWorkspaceFolder>();
}
server.reset(new LSPClientServer(cmdline, root, realLangId, serverConfig.value(QStringLiteral("initializationOptions")), folders));
connect(server.data(), &LSPClientServer::stateChanged, this, &self_type::onStateChanged, Qt::UniqueConnection);
if (!server->start()) {
QString message = i18n("Failed to start server: %1", cmdline.join(QLatin1Char(' ')));
const auto url = serverConfig.value(QStringLiteral("url")).toString();
if (!url.isEmpty()) {
message += QStringLiteral("\n") + i18n("Please check your PATH for the binary");
message += QStringLiteral("\n") + i18n("See also %1 for installation or details", url);
}
showMessage(message, KTextEditor::Message::Warning);
} else {
showMessage(i18n("Started server %2: %1", cmdline.join(QLatin1Char(' ')), serverDescription(server.data())),
KTextEditor::Message::Positive);
using namespace std::placeholders;
server->connect(server.data(), &LSPClientServer::logMessage, this, std::bind(&self_type::onMessage, this, true, _1));
server->connect(server.data(), &LSPClientServer::showMessage, this, std::bind(&self_type::onMessage, this, false, _1));
server->connect(server.data(), &LSPClientServer::workDoneProgress, this, &self_type::onWorkDoneProgress);
server->connect(server.data(), &LSPClientServer::workspaceFolders, this, &self_type::onWorkspaceFolders, Qt::UniqueConnection);
}
}
} else {
// we didn't find the server binary at all!
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment