#include "importController.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "core/utils/containerEnum.h" #include "core/utils/containers/containerUtils.h" #include "core/utils/protocolEnum.h" #include "core/utils/api/apiEnums.h" #include "core/utils/constants/apiKeys.h" #include "core/utils/constants/apiConstants.h" #include "core/utils/api/apiUtils.h" #include "core/utils/serialization/serialization.h" #include "core/utils/utilities.h" #include "core/utils/protocolEnum.h" #include "core/protocols/protocolUtils.h" #include "core/utils/constants/configKeys.h" #include "core/utils/constants/protocolConstants.h" #include "core/utils/qrCodeUtils.h" #include "core/models/serverConfig.h" using namespace amnezia; using namespace ProtocolUtils; namespace { ConfigTypes checkConfigFormat(const QString &config) { const QString openVpnConfigPatternCli = "client"; const QString openVpnConfigPatternDriver1 = "dev tun"; const QString openVpnConfigPatternDriver2 = "dev tap"; const QString wireguardConfigPatternSectionInterface = "[Interface]"; const QString wireguardConfigPatternSectionPeer = "[Peer]"; const QString xrayConfigPatternInbound = "inbounds"; const QString xrayConfigPatternOutbound = "outbounds"; const QString amneziaConfigPattern = "containers"; const QString amneziaConfigPatternHostName = "hostName"; const QString amneziaConfigPatternUserName = "userName"; const QString amneziaConfigPatternPassword = "password"; const QString amneziaFreeConfigPattern = "api_key"; const QString amneziaPremiumConfigPattern = "auth_data"; const QString backupPattern = "Servers/serversList"; if (config.contains(backupPattern)) { return ConfigTypes::Backup; } else if (config.contains(amneziaConfigPattern) || config.contains(amneziaFreeConfigPattern) || config.contains(amneziaPremiumConfigPattern) || (config.contains(amneziaConfigPatternHostName) && config.contains(amneziaConfigPatternUserName) && config.contains(amneziaConfigPatternPassword))) { return ConfigTypes::Amnezia; } else if (config.contains(wireguardConfigPatternSectionInterface) && config.contains(wireguardConfigPatternSectionPeer)) { return ConfigTypes::WireGuard; } else if ((config.contains(xrayConfigPatternInbound)) && (config.contains(xrayConfigPatternOutbound))) { return ConfigTypes::Xray; } else if (config.contains(openVpnConfigPatternCli) && (config.contains(openVpnConfigPatternDriver1) || config.contains(openVpnConfigPatternDriver2))) { return ConfigTypes::OpenVpn; } return ConfigTypes::Invalid; } } // namespace ImportController::ImportController(SecureServersRepository* serversRepository, SecureAppSettingsRepository* appSettingsRepository, QObject *parent) : QObject(parent), m_serversRepository(serversRepository), m_appSettingsRepository(appSettingsRepository) { } ImportController::ImportResult ImportController::extractConfigFromData(const QString &data, const QString &configFileName) { ImportResult result; result.configFileName = configFileName; result.maliciousWarningText.clear(); QString config = data; QString prefix; QString errormsg; ConfigTypes configType = ConfigTypes::Invalid; if (config.startsWith("vless://")) { configType = ConfigTypes::Xray; result.config = extractXrayConfig( Utils::JsonToString(serialization::vless::Deserialize(config, &prefix, &errormsg), QJsonDocument::JsonFormat::Compact), configType, prefix); if (!result.config.empty()) { result.configType = configType; return result; } } if (config.startsWith("vmess://") && config.contains("@")) { configType = ConfigTypes::Xray; result.config = extractXrayConfig( Utils::JsonToString(serialization::vmess_new::Deserialize(config, &prefix, &errormsg), QJsonDocument::JsonFormat::Compact), configType, prefix); if (!result.config.empty()) { result.configType = configType; return result; } } if (config.startsWith("vmess://")) { configType = ConfigTypes::Xray; result.config = extractXrayConfig( Utils::JsonToString(serialization::vmess::Deserialize(config, &prefix, &errormsg), QJsonDocument::JsonFormat::Compact), configType, prefix); if (!result.config.empty()) { result.configType = configType; return result; } } if (config.startsWith("trojan://")) { configType = ConfigTypes::Xray; result.config = extractXrayConfig( Utils::JsonToString(serialization::trojan::Deserialize(config, &prefix, &errormsg), QJsonDocument::JsonFormat::Compact), configType, prefix); if (!result.config.empty()) { result.configType = configType; return result; } } if (config.startsWith("ss://") && !config.contains("plugin=")) { configType = ConfigTypes::ShadowSocks; result.config = extractXrayConfig( Utils::JsonToString(serialization::ss::Deserialize(config, &prefix, &errormsg), QJsonDocument::JsonFormat::Compact), configType, prefix); if (!result.config.empty()) { result.configType = configType; return result; } } if (config.startsWith("ssd://")) { QStringList tmp; QList> servers = serialization::ssd::Deserialize(config, &prefix, &tmp); configType = ConfigTypes::ShadowSocks; // Took only first config from list if (!servers.isEmpty()) { result.config = extractXrayConfig(servers.first().first, configType); } if (!result.config.empty()) { result.configType = configType; return result; } } configType = checkConfigFormat(config); if (configType == ConfigTypes::Invalid) { config.replace("vpn://", ""); QByteArray ba = QByteArray::fromBase64(config.toUtf8(), QByteArray::Base64UrlEncoding | QByteArray::OmitTrailingEquals); QByteArray baUncompressed = qUncompress(ba); if (!baUncompressed.isEmpty()) { ba = baUncompressed; } config = ba; configType = checkConfigFormat(config); } result.configType = configType; switch (configType) { case ConfigTypes::OpenVpn: { result.config = extractOpenVpnConfig(config); if (!result.config.empty()) { checkForMaliciousStrings(result.config, result.maliciousWarningText); return result; } result.errorCode = ErrorCode::ImportInvalidConfigError; return result; } case ConfigTypes::Awg: case ConfigTypes::WireGuard: { result.config = extractWireGuardConfig(config, result.configType); result.isNativeWireGuardConfig = (result.configType == ConfigTypes::WireGuard); if (!result.config.empty()) { return result; } result.errorCode = ErrorCode::ImportInvalidConfigError; return result; } case ConfigTypes::Xray: { result.config = extractXrayConfig(config, configType); if (!result.config.empty()) { return result; } result.errorCode = ErrorCode::ImportInvalidConfigError; return result; } case ConfigTypes::Amnezia: { result.config = QJsonDocument::fromJson(config.toUtf8()).object(); if (apiUtils::isServerFromApi(result.config)) { auto apiConfig = result.config.value(apiDefs::key::apiConfig).toObject(); apiConfig[apiDefs::key::vpnKey] = data; result.config[apiDefs::key::apiConfig] = apiConfig; } processAmneziaConfig(result.config); if (!result.config.empty()) { checkForMaliciousStrings(result.config, result.maliciousWarningText); return result; } result.errorCode = ErrorCode::ImportInvalidConfigError; return result; } case ConfigTypes::Backup: { result.errorCode = ErrorCode::ImportBackupFileUseRestoreInstead; return result; } case ConfigTypes::Invalid: { result.errorCode = ErrorCode::ImportInvalidConfigError; result.configFileName.clear(); return result; } } result.errorCode = ErrorCode::ImportInvalidConfigError; return result; } ImportController::ImportResult ImportController::extractConfigFromQr(const QByteArray &data) { ImportResult result; QString dataStr = QString::fromUtf8(data); ConfigTypes configType = checkConfigFormat(dataStr); if (configType != ConfigTypes::Invalid) { return extractConfigFromData(dataStr, ""); } QJsonObject dataObj = QJsonDocument::fromJson(data).object(); if (!dataObj.isEmpty()) { result.config = dataObj; result.configType = ConfigTypes::Amnezia; return result; } QByteArray ba_uncompressed = qUncompress(data); if (!ba_uncompressed.isEmpty()) { result.config = QJsonDocument::fromJson(ba_uncompressed).object(); if (result.config.isEmpty()) { result.errorCode = ErrorCode::ImportInvalidConfigError; return result; } result.configType = ConfigTypes::Amnezia; return result; } QByteArray ba = QByteArray::fromBase64(data, QByteArray::Base64UrlEncoding | QByteArray::OmitTrailingEquals); QByteArray baUncompressed = qUncompress(ba); if (!baUncompressed.isEmpty()) { ba = baUncompressed; } if (!ba.isEmpty()) { result.config = QJsonDocument::fromJson(ba).object(); if (result.config.isEmpty()) { result.errorCode = ErrorCode::ImportInvalidConfigError; return result; } result.configType = ConfigTypes::Amnezia; return result; } result.errorCode = ErrorCode::ImportInvalidConfigError; return result; } void ImportController::startDecodingQr() { m_qrCodeChunks.clear(); m_totalQrCodeChunksCount = 0; m_receivedQrCodeChunksCount = 0; m_isQrCodeProcessed = true; } ImportController::QrParseResult ImportController::parseQrCodeChunk(const QString &code) { QrParseResult parseResult; parseResult.chunksReceived = m_receivedQrCodeChunksCount; parseResult.chunksTotal = m_totalQrCodeChunksCount; if (!m_isQrCodeProcessed) { return parseResult; } QByteArray ba = QByteArray::fromBase64(code.toUtf8(), QByteArray::Base64UrlEncoding | QByteArray::OmitTrailingEquals); QDataStream s(&ba, QIODevice::ReadOnly); qint16 magic; s >> magic; if (magic == qrCodeUtils::qrMagicCode) { quint8 chunksCount; s >> chunksCount; if (m_totalQrCodeChunksCount != chunksCount) { m_qrCodeChunks.clear(); } m_totalQrCodeChunksCount = chunksCount; quint8 chunkId; s >> chunkId; s >> m_qrCodeChunks[chunkId]; m_receivedQrCodeChunksCount = m_qrCodeChunks.size(); parseResult.chunksReceived = m_receivedQrCodeChunksCount; parseResult.chunksTotal = m_totalQrCodeChunksCount; if (m_qrCodeChunks.size() == m_totalQrCodeChunksCount) { QByteArray data; for (int i = 0; i < m_totalQrCodeChunksCount; ++i) { data.append(m_qrCodeChunks.value(i)); } ImportResult result = extractConfigFromQr(data); if (result.errorCode == ErrorCode::NoError) { parseResult.success = true; parseResult.importResult = result; m_isQrCodeProcessed = false; } else { m_qrCodeChunks.clear(); m_totalQrCodeChunksCount = 0; m_receivedQrCodeChunksCount = 0; } } } else { ImportResult result = extractConfigFromQr(code.toUtf8()); if (result.errorCode != ErrorCode::NoError) { result = extractConfigFromQr(ba); } if (result.errorCode == ErrorCode::NoError) { parseResult.success = true; parseResult.importResult = result; m_isQrCodeProcessed = false; } } return parseResult; } bool ImportController::isQrDecodingActive() const { return m_isQrCodeProcessed; } int ImportController::qrChunksReceived() const { return m_receivedQrCodeChunksCount; } int ImportController::qrChunksTotal() const { return m_totalQrCodeChunksCount; } void ImportController::importConfig(const QJsonObject &config) { ServerCredentials credentials; credentials.hostName = config.value(configKey::hostName).toString(); credentials.port = config.value(configKey::port).toInt(); credentials.userName = config.value(configKey::userName).toString(); credentials.secretData = config.value(configKey::password).toString(); if (credentials.isValid() || config.contains(configKey::containers)) { ServerConfig serverConfig = ServerConfig::fromJson(config); m_serversRepository->addServer(serverConfig); emit importFinished(); } else if (config.contains(configKey::configVersion)) { quint16 crc = qChecksum(QJsonDocument(config).toJson()); if (m_serversRepository->hasServerWithCrc(crc)) { emit importErrorOccurred(ErrorCode::ApiConfigAlreadyAdded, true); } else { QJsonObject configWithCrc = config; configWithCrc.insert(configKey::crc, crc); ServerConfig serverConfig = ServerConfig::fromJson(configWithCrc); m_serversRepository->addServer(serverConfig); emit importFinished(); } } else { qDebug() << "Failed to import profile"; qDebug().noquote() << QJsonDocument(config).toJson(); emit importErrorOccurred(ErrorCode::ImportInvalidConfigError, false); } } QJsonObject ImportController::processNativeWireGuardConfig(const QJsonObject &config) { QJsonObject result = config; auto containers = result.value(configKey::containers).toArray(); if (!containers.isEmpty()) { auto container = containers.at(0).toObject(); auto serverProtocolConfig = container.value(ContainerUtils::containerTypeToProtocolString(DockerContainer::WireGuard)).toObject(); auto clientProtocolConfig = QJsonDocument::fromJson(serverProtocolConfig.value(configKey::lastConfig).toString().toUtf8()).object(); QString junkPacketCount = QString::number(QRandomGenerator::global()->bounded(4, 7)); QString junkPacketMinSize = QString::number(10); QString junkPacketMaxSize = QString::number(50); clientProtocolConfig[configKey::junkPacketCount] = junkPacketCount; clientProtocolConfig[configKey::junkPacketMinSize] = junkPacketMinSize; clientProtocolConfig[configKey::junkPacketMaxSize] = junkPacketMaxSize; clientProtocolConfig[configKey::initPacketJunkSize] = "0"; clientProtocolConfig[configKey::responsePacketJunkSize] = "0"; clientProtocolConfig[configKey::initPacketMagicHeader] = "1"; clientProtocolConfig[configKey::responsePacketMagicHeader] = "2"; clientProtocolConfig[configKey::underloadPacketMagicHeader] = "3"; clientProtocolConfig[configKey::transportPacketMagicHeader] = "4"; clientProtocolConfig[configKey::cookieReplyPacketJunkSize] = "0"; clientProtocolConfig[configKey::transportPacketJunkSize] = "0"; clientProtocolConfig[configKey::specialJunk1] = protocols::awg::defaultSpecialJunk1; clientProtocolConfig[configKey::isObfuscationEnabled] = true; serverProtocolConfig[configKey::lastConfig] = QString(QJsonDocument(clientProtocolConfig).toJson()); container[configKey::wireguard] = serverProtocolConfig; containers.replace(0, container); result[configKey::containers] = containers; } return result; } ConfigTypes ImportController::checkConfigFormat(const QString &config) const { return ::checkConfigFormat(config); } QJsonObject ImportController::extractOpenVpnConfig(const QString &data) const { QJsonObject openVpnConfig; openVpnConfig[configKey::config] = data; QJsonObject lastConfig; lastConfig[configKey::lastConfig] = QString(QJsonDocument(openVpnConfig).toJson()); lastConfig[configKey::isThirdPartyConfig] = true; QJsonObject containers; containers.insert(configKey::container, QJsonValue(configKey::amneziaOpenvpn)); containers.insert(configKey::openvpn, QJsonValue(lastConfig)); QJsonArray arr; arr.push_back(containers); QString hostName; const static QRegularExpression hostNameRegExp("remote\\s+([^\\s]+)"); QRegularExpressionMatch hostNameMatch = hostNameRegExp.match(data); if (hostNameMatch.hasMatch()) { hostName = hostNameMatch.captured(1); } QJsonObject config; config[configKey::containers] = arr; config[configKey::defaultContainer] = configKey::amneziaOpenvpn; config[configKey::description] = m_appSettingsRepository->nextAvailableServerName(); const static QRegularExpression dnsRegExp("dhcp-option DNS (\\b\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\b)"); QRegularExpressionMatchIterator dnsMatch = dnsRegExp.globalMatch(data); if (dnsMatch.hasNext()) { config[configKey::dns1] = dnsMatch.next().captured(1); } if (dnsMatch.hasNext()) { config[configKey::dns2] = dnsMatch.next().captured(1); } config[configKey::hostName] = hostName; return config; } QJsonObject ImportController::extractWireGuardConfig(const QString &data, ConfigTypes &configType) const { QMap configMap; auto configByLines = data.split("\n"); for (const QString &line : configByLines) { QString trimmedLine = line.trimmed(); if (trimmedLine.startsWith("[") && trimmedLine.endsWith("]")) { continue; } else { QStringList parts = trimmedLine.split(" = "); if (parts.count() == 2) { configMap[parts.at(0).trimmed()] = parts.at(1).trimmed(); } } } QJsonObject lastConfig; lastConfig[configKey::config] = data; auto url { QUrl::fromUserInput(configMap.value(protocols::wireguard::Endpoint)) }; QString hostName; QString port; if (!url.host().isEmpty()) { hostName = url.host(); } else { qDebug() << "Key parameter" << protocols::wireguard::Endpoint << "is missing or has an invalid format"; return QJsonObject(); } if (url.port() != -1) { port = QString::number(url.port()); } else { port = protocols::wireguard::defaultPort; } lastConfig[configKey::hostName] = hostName; lastConfig[configKey::port] = port.toInt(); if (!configMap.value(protocols::wireguard::PrivateKey).isEmpty() && !configMap.value(protocols::wireguard::Address).isEmpty() && !configMap.value(protocols::wireguard::PublicKey).isEmpty()) { lastConfig[configKey::clientPrivKey] = configMap.value(protocols::wireguard::PrivateKey); lastConfig[configKey::clientIp] = configMap.value(protocols::wireguard::Address); if (!configMap.value(protocols::wireguard::PresharedKey).isEmpty()) { lastConfig[configKey::pskKey] = configMap.value(protocols::wireguard::PresharedKey); } else if (!configMap.value(protocols::wireguard::PreSharedKey).isEmpty()) { lastConfig[configKey::pskKey] = configMap.value(protocols::wireguard::PreSharedKey); } lastConfig[configKey::serverPubKey] = configMap.value(protocols::wireguard::PublicKey); } else { qDebug() << "One of the key parameters is missing (PrivateKey, Address, PublicKey)"; return QJsonObject(); } if (!configMap.value(protocols::wireguard::MTU).isEmpty()) { lastConfig[configKey::mtu] = configMap.value(protocols::wireguard::MTU); } if (!configMap.value(protocols::wireguard::PersistentKeepalive).isEmpty()) { lastConfig[configKey::persistentKeepAlive] = configMap.value(protocols::wireguard::PersistentKeepalive); } QJsonArray allowedIpsJsonArray = QJsonArray::fromStringList( configMap.value(protocols::wireguard::AllowedIPs).split(", ")); lastConfig[configKey::allowedIps] = allowedIpsJsonArray; QString protocolName = configKey::wireguard; QString protocolVersion; ConfigTypes detectedType = ConfigTypes::WireGuard; const QStringList requiredJunkFields = { configKey::junkPacketCount, configKey::junkPacketMinSize, configKey::junkPacketMaxSize, configKey::initPacketJunkSize, configKey::responsePacketJunkSize, configKey::initPacketMagicHeader, configKey::responsePacketMagicHeader, configKey::underloadPacketMagicHeader, configKey::transportPacketMagicHeader }; const QStringList optionalJunkFields = { configKey::cookieReplyPacketJunkSize, configKey::transportPacketJunkSize, configKey::specialJunk1, configKey::specialJunk2, configKey::specialJunk3, configKey::specialJunk4, configKey::specialJunk5 }; bool hasAllRequiredFields = std::all_of(requiredJunkFields.begin(), requiredJunkFields.end(), [&configMap](const QString &field) { return !configMap.value(field).isEmpty(); }); if (hasAllRequiredFields) { for (const QString &field : requiredJunkFields) { lastConfig[field] = configMap.value(field); } for (const QString &field : optionalJunkFields) { if (!configMap.value(field).isEmpty()) { lastConfig[field] = configMap.value(field); } } bool hasCookieReplyPacketJunkSize = !configMap.value(configKey::cookieReplyPacketJunkSize).isEmpty(); bool hasTransportPacketJunkSize = !configMap.value(configKey::transportPacketJunkSize).isEmpty(); bool hasSpecialJunk = !configMap.value(configKey::specialJunk1).isEmpty() || !configMap.value(configKey::specialJunk2).isEmpty() || !configMap.value(configKey::specialJunk3).isEmpty() || !configMap.value(configKey::specialJunk4).isEmpty() || !configMap.value(configKey::specialJunk5).isEmpty(); if (hasCookieReplyPacketJunkSize && hasTransportPacketJunkSize) { protocolVersion = "2"; } else if (hasSpecialJunk && !hasCookieReplyPacketJunkSize && !hasTransportPacketJunkSize) { protocolVersion = "1.5"; } protocolName = configKey::awg; detectedType = ConfigTypes::Awg; } if (!configMap.value(protocols::wireguard::MTU).isEmpty()) { lastConfig[configKey::mtu] = configMap.value(protocols::wireguard::MTU); } else { lastConfig[configKey::mtu] = (protocolName == configKey::awg) ? protocols::awg::defaultMtu : protocols::wireguard::defaultMtu; } QJsonObject wireguardConfig; wireguardConfig[configKey::lastConfig] = QString(QJsonDocument(lastConfig).toJson()); wireguardConfig[configKey::isThirdPartyConfig] = true; wireguardConfig[configKey::port] = port; wireguardConfig[configKey::transportProto] = protocols::openvpn::defaultTransportProto; if (protocolName == configKey::awg && !protocolVersion.isEmpty()) { wireguardConfig[configKey::protocolVersion] = protocolVersion; } QJsonObject containers; QString containerName = (protocolName == configKey::awg) ? configKey::amneziaAwg : configKey::amneziaWireguard; containers.insert(configKey::container, QJsonValue(containerName)); containers.insert(protocolName, QJsonValue(wireguardConfig)); QJsonArray arr; arr.push_back(containers); QJsonObject config; config[configKey::containers] = arr; config[configKey::defaultContainer] = containerName; config[configKey::description] = m_appSettingsRepository->nextAvailableServerName(); const static QRegularExpression dnsRegExp( "DNS = " "(\\b\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\b).*(\\b\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\b)"); QRegularExpressionMatch dnsMatch = dnsRegExp.match(data); if (dnsMatch.hasMatch()) { config[configKey::dns1] = dnsMatch.captured(1); config[configKey::dns2] = dnsMatch.captured(2); } config[configKey::hostName] = hostName; configType = detectedType; return config; } QJsonObject ImportController::extractXrayConfig(const QString &data, ConfigTypes configType, const QString &description) const { QJsonParseError parserErr; QJsonDocument jsonConf = QJsonDocument::fromJson(data.toLocal8Bit(), &parserErr); QJsonObject xrayVpnConfig; xrayVpnConfig[configKey::config] = jsonConf.toJson().constData(); QJsonObject lastConfig; lastConfig[configKey::lastConfig] = jsonConf.toJson().constData(); lastConfig[configKey::isThirdPartyConfig] = true; QJsonObject containers; if (configType == ConfigTypes::ShadowSocks) { containers.insert(configKey::ssxray, QJsonValue(lastConfig)); containers.insert(configKey::container, QJsonValue(configKey::amneziaSsxray)); } else { containers.insert(configKey::container, QJsonValue(configKey::amneziaXray)); containers.insert(configKey::xray, QJsonValue(lastConfig)); } QJsonArray arr; arr.push_back(containers); QString hostName; const static QRegularExpression hostNameRegExp("\"address\":\\s*\"([^\"]+)"); QRegularExpressionMatch hostNameMatch = hostNameRegExp.match(data); if (hostNameMatch.hasMatch()) { hostName = hostNameMatch.captured(1); } QJsonObject config; config[configKey::containers] = arr; config[configKey::defaultContainer] = (configType == ConfigTypes::ShadowSocks) ? configKey::amneziaSsxray : configKey::amneziaXray; if (description.isEmpty()) { config[configKey::description] = m_appSettingsRepository->nextAvailableServerName(); } else { config[configKey::description] = description; } config[configKey::hostName] = hostName; return config; } void ImportController::checkForMaliciousStrings(const QJsonObject &serverConfig, QString &warningText) const { const QJsonArray &containers = serverConfig.value(configKey::containers).toArray(); for (const QJsonValue &container : containers) { auto containerConfig = container.toObject(); auto containerName = containerConfig[configKey::container].toString(); if (containerName == ContainerUtils::containerToString(DockerContainer::OpenVpn)) { QString protocolConfig = containerConfig[ProtocolUtils::protoToString(Proto::OpenVpn)].toObject()[configKey::lastConfig].toString(); QString protocolConfigJson = QJsonDocument::fromJson(protocolConfig.toUtf8()).object()[configKey::config].toString(); // https://github.com/OpenVPN/openvpn/blob/master/doc/man-sections/script-options.rst QStringList dangerousTags { "up", "tls-verify", "ipchange", "client-connect", "route-up", "route-pre-down", "client-disconnect", "down", "learn-address", "auth-user-pass-verify" }; QStringList maliciousStrings; QStringList lines = protocolConfigJson.split('\n', Qt::SkipEmptyParts); for (const QString &rawLine : lines) { QString line = rawLine.trimmed(); QString command = line.section(' ', 0, 0, QString::SectionSkipEmpty); if (dangerousTags.contains(command, Qt::CaseInsensitive)) { maliciousStrings << rawLine; } } warningText = "This configuration contains an OpenVPN setup. OpenVPN configurations can include malicious " "scripts, so only add it if you fully trust the provider of this config. "; if (!maliciousStrings.isEmpty()) { warningText += "
In the imported configuration, potentially dangerous lines were found:"; for (const auto &string : maliciousStrings) { warningText += QString("
%1").arg(string); } } } } } void ImportController::processAmneziaConfig(QJsonObject &config) const { auto containers = config.value(configKey::containers).toArray(); for (auto i = 0; i < containers.size(); i++) { auto container = containers.at(i).toObject(); auto dockerContainer = ContainerUtils::containerFromString(container.value(configKey::container).toString()); if (ContainerUtils::isAwgContainer(dockerContainer) || dockerContainer == DockerContainer::WireGuard) { auto containerConfig = container.value(ContainerUtils::containerTypeToProtocolString(dockerContainer)).toObject(); auto protocolConfig = containerConfig.value(configKey::lastConfig).toString(); if (protocolConfig.isEmpty()) { return; } QJsonObject jsonConfig = QJsonDocument::fromJson(protocolConfig.toUtf8()).object(); jsonConfig[configKey::mtu] = ContainerUtils::isAwgContainer(dockerContainer) ? protocols::awg::defaultMtu : protocols::wireguard::defaultMtu; containerConfig[configKey::lastConfig] = QString(QJsonDocument(jsonConfig).toJson()); container[ContainerUtils::containerTypeToProtocolString(dockerContainer)] = containerConfig; containers.replace(i, container); config.insert(configKey::containers, containers); } } }