From 17e457bfccac6c95dc80ddab459121d63634792b Mon Sep 17 00:00:00 2001 From: Raul Metsma Date: Mon, 17 Aug 2026 10:34:20 +0300 Subject: [PATCH] Harden CDoc1 orig_file handling IB-9046 Signed-off-by: Raul Metsma --- client/CDocSupport.cpp | 93 ++++++++++++++++++++++++++++++------------ client/CryptoDoc.cpp | 8 ++-- 2 files changed, 73 insertions(+), 28 deletions(-) diff --git a/client/CDocSupport.cpp b/client/CDocSupport.cpp index 19cf47812..a170373b5 100644 --- a/client/CDocSupport.cpp +++ b/client/CDocSupport.cpp @@ -27,6 +27,7 @@ #include #include #include +#include #include #include "Application.h" @@ -56,39 +57,81 @@ static QByteArray toByteArray(const std::vector &data) { std::vector CDocSupport::getCDocFileList(const QString &filename) { + static constexpr qsizetype MAX_CONTENT_LENGTH = 4096; + static constexpr size_t MAX_ENTRIES = 1'000; + static constexpr qsizetype ORIG_FILE_FIELDS = 4; + std::vector files; - if (libcdoc::CDocReader::getCDocFileVersion(filename.toStdString()) != 1) - return files; QFile ifs(filename); if(!ifs.open(QIODevice::ReadOnly)) return files; QXmlStreamReader xml(&ifs); - while (xml.readNextStartElement()) { - if (xml.name() == QLatin1String("EncryptedData")) { - while (xml.readNextStartElement()) { - if (xml.name() == QLatin1String("EncryptionProperties")) { - while (xml.readNextStartElement()) { - if (xml.name() == QLatin1String("EncryptionProperty")) { - if (xml.attributes().value(QStringLiteral("Name")) == QLatin1String("orig_file")) { - QString content = xml.readElementText(); - auto list = content.split('|'); - files.push_back({list.at(0).toStdString(), list.at(1).toInt()}); - } else { - xml.skipCurrentElement(); - } - } else { - xml.skipCurrentElement(); - } - } - } else { - xml.skipCurrentElement(); - } - } - } else { + auto readNextElement = [&xml](const QString &name) { + while (xml.readNextStartElement()) { + if (xml.name() == name) + return true; xml.skipCurrentElement(); } + return false; + }; + auto readElementText = [&xml]() -> std::optional { + QString content; + bool usable = true; + while(!xml.atEnd()) { + switch(xml.readNext()) { + case QXmlStreamReader::Characters: { + QStringView text = xml.text(); + if(usable && text.size() <= MAX_CONTENT_LENGTH - content.size()) + content += text; + else + usable = false; + break; + } + case QXmlStreamReader::Comment: + case QXmlStreamReader::ProcessingInstruction: + break; + case QXmlStreamReader::EndElement: + return usable ? std::optional{std::move(content)} : std::nullopt; + case QXmlStreamReader::StartElement: + usable = false; + xml.skipCurrentElement(); + break; + case QXmlStreamReader::EntityReference: + usable = false; + break; + default: + return std::nullopt; + } + } + return std::nullopt; + }; + while(!xml.atEnd() && !xml.isStartElement()) { + if(xml.readNext() == QXmlStreamReader::DTD) + return files; + } + if(!xml.isStartElement() || xml.name() != QLatin1String("EncryptedData")) + return files; + while(readNextElement(QLatin1String("EncryptionProperties"))) { + while(readNextElement(QLatin1String("EncryptionProperty"))) { + if(xml.attributes().value(QStringLiteral("Name")) != QLatin1String("orig_file")) { + xml.skipCurrentElement(); + continue; + } + if(files.size() >= MAX_ENTRIES) { + xml.skipCurrentElement(); + continue; + } + auto content = readElementText(); + if(!content) + continue; + QStringList list = content->split('|'); + bool ok = false; + qint64 size = list.size() == ORIG_FILE_FIELDS ? list.at(1).toLongLong(&ok) : 0; + if(list.size() != ORIG_FILE_FIELDS || !ok || size < 0) + continue; + files.emplace_back(list.at(0).toStdString(), size); + } } - return files; } diff --git a/client/CryptoDoc.cpp b/client/CryptoDoc.cpp index a83b10d91..a559dd63e 100644 --- a/client/CryptoDoc.cpp +++ b/client/CryptoDoc.cpp @@ -498,9 +498,11 @@ bool CryptoDoc::open(const QString &file) return false; } d->version = d->reader->version; - std::vector files = CDocSupport::getCDocFileList(file); - for (auto& f : files) { - d->files.push_back({f.name, {}, f.size, {}}); + if (d->version == 1) { + std::vector files = CDocSupport::getCDocFileList(file); + for (auto& f : files) { + d->files.push_back({std::move(f.name), {}, f.size, {}}); + } } Application::addRecent( file ); return true;