Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
93 changes: 81 additions & 12 deletions src/libsync/foldermetadata.cpp
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
/*
* SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: GPL-2.0-or-later
Expand All @@ -9,6 +9,7 @@
#include "clientsideencryption.h"
#include "clientsideencryptionjobs.h"
#include <common/checksums.h>
#include <QDir>
#include <QJsonArray>
#include <QJsonDocument>
#include <QSslCertificate>
Expand Down Expand Up @@ -49,12 +50,33 @@
}
}

bool FolderMetadata::isOriginalFilenameValid(const QString &originalFilename)
{
if (originalFilename.isEmpty()) {
return false;
}

if (originalFilename == QStringLiteral(".")
|| originalFilename == QStringLiteral("..")) {
return false;
}

if (originalFilename.contains(QLatin1Char('/'))
|| originalFilename.contains(QLatin1Char('\\'))
|| originalFilename.contains(QChar(0))) {
return false;
}

const auto slashPrefixedName = QStringLiteral("/") + originalFilename;
return QDir::cleanPath(slashPrefixedName) == slashPrefixedName;
}

bool FolderMetadata::EncryptedFile::isDirectory() const
{
return mimetype.isEmpty() || mimetype == QByteArrayLiteral("inode/directory") || mimetype == QByteArrayLiteral("httpd/unix-directory");
}

FolderMetadata::FolderMetadata(AccountPtr account, const QString &remoteFolderRoot, FolderType folderType) :

Check warning on line 79 in src/libsync/foldermetadata.cpp

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Unmodified variable "account" of type "class QSharedPointer<class OCC::Account>" should be const-qualified.

See more on https://sonarcloud.io/project/issues?id=nextcloud_desktop&issues=AZ7_FsAliYrk9yL2a3hc&open=AZ7_FsAliYrk9yL2a3hc&pullRequest=10238
_account(account),
_remoteFolderRoot(Utility::noLeadingSlashPath(Utility::noTrailingSlashPath(remoteFolderRoot))),
_isRootEncryptedFolder(folderType == FolderType::Root)
Expand All @@ -63,12 +85,12 @@
initEmptyMetadata();
}

FolderMetadata::FolderMetadata(AccountPtr account,

Check warning on line 88 in src/libsync/foldermetadata.cpp

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Unmodified variable "account" of type "class QSharedPointer<class OCC::Account>" should be const-qualified.

See more on https://sonarcloud.io/project/issues?id=nextcloud_desktop&issues=AZ7_FsAliYrk9yL2a3he&open=AZ7_FsAliYrk9yL2a3he&pullRequest=10238
const QString &remoteFolderRoot,
const QByteArray &metadata,
const RootEncryptedFolderInfo &rootEncryptedFolderInfo,
const QByteArray &signature,
QObject *parent)

Check warning on line 93 in src/libsync/foldermetadata.cpp

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Unmodified variable "folderType" of type "enum OCC::FolderMetadata::FolderType" should be const-qualified.

See more on https://sonarcloud.io/project/issues?id=nextcloud_desktop&issues=AZ7_FsAliYrk9yL2a3hf&open=AZ7_FsAliYrk9yL2a3hf&pullRequest=10238
: QObject(parent)
, _account(account)
, _remoteFolderRoot(Utility::noLeadingSlashPath(Utility::noTrailingSlashPath(remoteFolderRoot)))
Expand Down Expand Up @@ -119,6 +141,11 @@
_account->reportClientStatus(OCC::ClientStatusReportingStatus::E2EeError_GeneralError);
return;
}
if (_existingMetadataVersion < MetadataVersion::Version2_0 && !_initialSignature.isEmpty()) {
qCWarning(lcCseMetadata()) << "Could not setup legacy metadata with a V2 signature.";
_account->reportClientStatus(OCC::ClientStatusReportingStatus::E2EeError_GeneralError);
return;
}
if (_existingMetadataVersion < MetadataVersion::Version2_0) {
setupExistingMetadataLegacy(metadata);
return;
Expand Down Expand Up @@ -155,8 +182,8 @@
folderUser.certificatePem = folderUserObject.value(usersCertificateKey).toString().toUtf8();
folderUser.encryptedMetadataKey = folderUserObject.value(usersEncryptedMetadataKey).toString().toUtf8();
_folderUsers[userId] = folderUser;
}

Check warning on line 186 in src/libsync/foldermetadata.cpp

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Complete the task associated to this "TODO" comment.

See more on https://sonarcloud.io/project/issues?id=nextcloud_desktop&issues=AZ7_FsAliYrk9yL2a3hZ&open=AZ7_FsAliYrk9yL2a3hZ&pullRequest=10238

Check warning on line 186 in src/libsync/foldermetadata.cpp

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Edit this comment to use the C++ format, i.e. "//".

See more on https://sonarcloud.io/project/issues?id=nextcloud_desktop&issues=AZ7_FsAliYrk9yL2a3hY&open=AZ7_FsAliYrk9yL2a3hY&pullRequest=10238
if (_isRootEncryptedFolder && !_initialSignature.isEmpty()) {
const auto metadataForSignature = prepareMetadataForSignature(metaDataDoc);

Expand Down Expand Up @@ -226,7 +253,7 @@
_keyChecksums.insert(keyChecksum);
}
}

Check warning on line 256 in src/libsync/foldermetadata.cpp

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Complete the task associated to this "TODO" comment.

See more on https://sonarcloud.io/project/issues?id=nextcloud_desktop&issues=AZ7_FsAliYrk9yL2a3ha&open=AZ7_FsAliYrk9yL2a3ha&pullRequest=10238
if (!verifyMetadataKey(binaryMetadataKeyForDecryption())) {
qCWarning(lcCseMetadata()) << "Could not verify metadataKey!";
_account->reportClientStatus(OCC::ClientStatusReportingStatus::E2EeError_GeneralError);
Expand All @@ -243,7 +270,7 @@
// What does that mean? We store the counter in metadata, should we now store it in local database as we do for all file records in SyncJournal?
// What if metadata was not updated for a while? The counter will then not be greater than locally stored (in SyncJournal DB?)
_counter = counterVariantFromJson.value<quint64>();
}

Check warning on line 273 in src/libsync/foldermetadata.cpp

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Use the init-statement to declare "counterVariantFromJson" inside the if statement.

See more on https://sonarcloud.io/project/issues?id=nextcloud_desktop&issues=AZ7_FsAliYrk9yL2a3hh&open=AZ7_FsAliYrk9yL2a3hh&pullRequest=10238

for (auto it = files.constBegin(), end = files.constEnd(); it != end; ++it) {
const auto parsedEncryptedFile = parseEncryptedFileFromJson(it.key(), it.value());
Expand All @@ -254,12 +281,20 @@

for (auto it = folders.constBegin(); it != folders.constEnd(); ++it) {
const auto folderName = it.value().toString();
if (!folderName.isEmpty()) {
EncryptedFile file;
file.encryptedFilename = it.key();
file.originalFilename = folderName;
_files.push_back(file);
if (folderName.isEmpty()) {
continue;
}

if (!isOriginalFilenameValid(folderName)) {
qCWarning(lcCseMetadata()) << "skipping encrypted folder" << it.key() << "metadata has an invalid file name";
_account->reportClientStatus(OCC::ClientStatusReportingStatus::E2EeError_GeneralError);
continue;
}

EncryptedFile file;
file.encryptedFilename = it.key();
file.originalFilename = folderName;
_files.push_back(file);
}
_isMetadataValid = true;
}
Expand All @@ -269,7 +304,7 @@
const auto doc = QJsonDocument::fromJson(metadata);
qCDebug(lcCseMetadata()) << "Setting up legacy existing metadata version" << _existingMetadataVersion << doc.toJson(QJsonDocument::Compact);

const auto &metaDataStr = metadataStringFromOCsDocument(doc);

Check failure on line 307 in src/libsync/foldermetadata.cpp

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Refactor this function to reduce its Cognitive Complexity from 30 to the 25 allowed.

See more on https://sonarcloud.io/project/issues?id=nextcloud_desktop&issues=AZ7_FsAliYrk9yL2a3hl&open=AZ7_FsAliYrk9yL2a3hl&pullRequest=10238
const auto &metaDataDoc = QJsonDocument::fromJson(metaDataStr.toLocal8Bit());
const auto &metaDataObj = metaDataDoc.object();
const auto &fullMetaDataObj = metaDataObj[metadataJsonKey].toObject();
Expand All @@ -283,7 +318,7 @@
const auto decryptedMetadataKeyBase64 = decryptDataWithPrivateKey(metadataKeyFromJson, _account->e2e()->certificateSha256Fingerprint());
if (!decryptedMetadataKeyBase64.isEmpty()) {
// fromBase64() multiple times just to stick with the old wrong way
_binaryMetadataKeyForDecryption = QByteArray::fromBase64(QByteArray::fromBase64(QByteArray::fromBase64(decryptedMetadataKeyBase64)));

Check warning on line 321 in src/libsync/foldermetadata.cpp

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Use the init-statement to declare "metadataKeyFromJson" inside the if statement.

See more on https://sonarcloud.io/project/issues?id=nextcloud_desktop&issues=AZ7_FsAliYrk9yL2a3hk&open=AZ7_FsAliYrk9yL2a3hk&pullRequest=10238
}
}

Expand All @@ -308,8 +343,8 @@
_binaryMetadataKeyForDecryption = QByteArray::fromBase64(QByteArray::fromBase64(lastMetadataKeyValueFromJsonBase64));
}
}
}

Check warning on line 346 in src/libsync/foldermetadata.cpp

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Rename this identifier to be shorter or equal to 31 characters.

See more on https://sonarcloud.io/project/issues?id=nextcloud_desktop&issues=AZ7_FsAliYrk9yL2a3hn&open=AZ7_FsAliYrk9yL2a3hn&pullRequest=10238
}

Check failure on line 347 in src/libsync/foldermetadata.cpp

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Refactor this code to not nest more than 3 if|for|do|while|switch statements.

See more on https://sonarcloud.io/project/issues?id=nextcloud_desktop&issues=AZ7_FsAliYrk9yL2a3hm&open=AZ7_FsAliYrk9yL2a3hm&pullRequest=10238

if (binaryMetadataKeyForDecryption().isEmpty()) {
qCWarning(lcCseMetadata()) << "Could not setup existing metadata with missing metadataKeys!";
Expand Down Expand Up @@ -344,12 +379,19 @@

const auto decryptedFileObj = decryptedFileDoc.object();

if (decryptedFileObj["filename"].toString().isEmpty()) {
const auto originalFilename = decryptedFileObj["filename"].toString();
if (originalFilename.isEmpty()) {
qCWarning(lcCseMetadata) << "decrypted metadata" << decryptedFileDoc.toJson(QJsonDocument::Compact) << "skipping encrypted file" << file.encryptedFilename << "metadata has an empty file name";
continue;
}

file.originalFilename = decryptedFileObj["filename"].toString();
if (!isOriginalFilenameValid(originalFilename)) {
qCWarning(lcCseMetadata) << "skipping encrypted file" << file.encryptedFilename << "metadata has an invalid file name";
_account->reportClientStatus(OCC::ClientStatusReportingStatus::E2EeError_GeneralError);
continue;
}

file.originalFilename = originalFilename;
file.encryptionKey = QByteArray::fromBase64(decryptedFileObj["key"].toString().toLocal8Bit());
file.mimetype = decryptedFileObj["mimetype"].toString().toLocal8Bit();

Expand Down Expand Up @@ -489,9 +531,9 @@
});
for (const auto &singleFile : sortedFiles) {
hashAlgorithm.addData(singleFile.encryptedFilename.toUtf8());
}
hashAlgorithm.addData(metadataKey);

Check warning on line 536 in src/libsync/foldermetadata.cpp

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Replace with the version of "std::ranges::sort" that takes a range.

See more on https://sonarcloud.io/project/issues?id=nextcloud_desktop&issues=AZ7_FsAliYrk9yL2a3hq&open=AZ7_FsAliYrk9yL2a3hq&pullRequest=10238
return hashAlgorithm.result().toHex();
}

Expand All @@ -503,10 +545,17 @@
FolderMetadata::EncryptedFile FolderMetadata::parseEncryptedFileFromJson(const QString &encryptedFilename, const QJsonValue &fileJSON) const
{
const auto fileObj = fileJSON.toObject();
if (fileObj["filename"].toString().isEmpty()) {
const auto originalFilename = fileObj["filename"].toString();
if (originalFilename.isEmpty()) {
qCWarning(lcCseMetadata()) << "skipping encrypted file" << encryptedFilename << "metadata has an empty file name";
return {};
}

if (!isOriginalFilenameValid(originalFilename)) {
qCWarning(lcCseMetadata()) << "skipping encrypted file" << encryptedFilename << "metadata has an invalid file name";
_account->reportClientStatus(OCC::ClientStatusReportingStatus::E2EeError_GeneralError);
return {};
Comment on lines +554 to +557

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Fail metadata setup when an encrypted name is unsafe

When an existing encrypted folder's metadata contains an invalid original name, this branch returns an empty EncryptedFile; setupExistingMetadata() then just skips appending it and still marks the metadata valid. In that scenario the corresponding server entry remains in the PROPFIND results without an e2eMangledName/encrypted flag, so discovery can treat the encrypted blob itself as a normal plaintext child under its mangled server name instead of failing the sync. Please make parsing fail the metadata setup, or otherwise filter that remote entry, rather than silently dropping the mapping.

Useful? React with 👍 / 👎.

}

EncryptedFile file;
file.encryptedFilename = encryptedFilename;
Expand All @@ -516,7 +565,7 @@
nonce = QByteArray::fromBase64(fileObj[nonceKey].toString().toLocal8Bit());
}
file.initializationVector = nonce;
file.originalFilename = fileObj["filename"].toString();
file.originalFilename = originalFilename;
file.encryptionKey = QByteArray::fromBase64(fileObj["key"].toString().toLocal8Bit());
file.mimetype = fileObj["mimetype"].toString().toLocal8Bit();

Expand All @@ -530,6 +579,11 @@

QJsonObject FolderMetadata::convertFileToJsonObject(const EncryptedFile *encryptedFile) const
{
if (!encryptedFile || !isOriginalFilenameValid(encryptedFile->originalFilename)) {
qCWarning(lcCseMetadata()) << "Metadata generation failed. Invalid original file name.";
return {};
}

Check warning on line 585 in src/libsync/foldermetadata.cpp

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Unmodified variable "encryptedFile" of type "const struct OCC::FolderMetadata::EncryptedFile *" should be const-qualified.

See more on https://sonarcloud.io/project/issues?id=nextcloud_desktop&issues=AZ7_FsAliYrk9yL2a3hr&open=AZ7_FsAliYrk9yL2a3hr&pullRequest=10238

QJsonObject file;
file.insert("key", QString(encryptedFile->encryptionKey.toBase64()));
file.insert("filename", encryptedFile->originalFilename);
Expand All @@ -548,7 +602,7 @@
return _binaryMetadataKeyForEncryption;
}

const QSet<QByteArray>& FolderMetadata::keyChecksums() const

Check warning on line 605 in src/libsync/foldermetadata.cpp

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Remove this "const" qualifier from the return type in all declarations.

See more on https://sonarcloud.io/project/issues?id=nextcloud_desktop&issues=AZ7_FsAliYrk9yL2a3hs&open=AZ7_FsAliYrk9yL2a3hs&pullRequest=10238
{
return _keyChecksums;
}
Expand Down Expand Up @@ -590,7 +644,7 @@
Q_ASSERT(_isMetadataValid);
if (!_isMetadataValid) {
qCWarning(lcCseMetadata()) << "Could not encrypt non-initialized metadata!";
return {};

Check failure on line 647 in src/libsync/foldermetadata.cpp

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Refactor this function to reduce its Cognitive Complexity from 36 to the 25 allowed.

See more on https://sonarcloud.io/project/issues?id=nextcloud_desktop&issues=AZ7_FsAliYrk9yL2a3ht&open=AZ7_FsAliYrk9yL2a3ht&pullRequest=10238
}

if (latestSupportedMetadataVersion() < MetadataVersion::Version2_0) {
Expand Down Expand Up @@ -622,9 +676,9 @@
const auto file = convertFileToJsonObject(&(*it));
if (file.isEmpty()) {
qCWarning(lcCseMetadata) << "Metadata generation failed for file" << it->encryptedFilename;
return {};

Check warning on line 679 in src/libsync/foldermetadata.cpp

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Define each identifier in a dedicated statement.

See more on https://sonarcloud.io/project/issues?id=nextcloud_desktop&issues=AZ7_FsAliYrk9yL2a3hu&open=AZ7_FsAliYrk9yL2a3hu&pullRequest=10238
}

Check warning on line 680 in src/libsync/foldermetadata.cpp

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Unmodified variable "end" of type "class QList<struct OCC::FolderMetadata::EncryptedFile>::const_iterator" should be const-qualified.

See more on https://sonarcloud.io/project/issues?id=nextcloud_desktop&issues=AZ7_FsAliYrk9yL2a3hv&open=AZ7_FsAliYrk9yL2a3hv&pullRequest=10238
const auto isDirectory =

Check warning on line 681 in src/libsync/foldermetadata.cpp

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Use "std::to_address" to convert iterator to pointer.

See more on https://sonarcloud.io/project/issues?id=nextcloud_desktop&issues=AZ7_FsAliYrk9yL2a3hw&open=AZ7_FsAliYrk9yL2a3hw&pullRequest=10238
it->mimetype.isEmpty() || it->mimetype == QByteArrayLiteral("inode/directory") || it->mimetype == QByteArrayLiteral("httpd/unix-directory");
if (isDirectory) {
folders.insert(it->encryptedFilename, it->originalFilename);
Expand All @@ -640,7 +694,7 @@
}
}

QJsonObject cipherText = {{counterKey, QJsonValue::fromVariant(newCounter())}, {filesKey, files}, {foldersKey, folders}};

Check warning on line 697 in src/libsync/foldermetadata.cpp

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Unmodified variable "end" of type "class QSet<class QByteArray>::const_iterator" should be const-qualified.

See more on https://sonarcloud.io/project/issues?id=nextcloud_desktop&issues=AZ7_FsAliYrk9yL2a3hx&open=AZ7_FsAliYrk9yL2a3hx&pullRequest=10238

const auto isChecksumsArrayValid = (!_isRootEncryptedFolder && keyChecksums.isEmpty()) || (_isRootEncryptedFolder && !keyChecksums.isEmpty());
Q_ASSERT(isChecksumsArrayValid);
Expand Down Expand Up @@ -723,6 +777,12 @@

QJsonObject files;
for (auto it = _files.constBegin(), end = _files.constEnd(); it != end; ++it) {
if (!isOriginalFilenameValid(it->originalFilename)) {
qCWarning(lcCseMetadata) << "Metadata generation failed. Invalid original file name for encrypted file" << it->encryptedFilename;
_account->reportClientStatus(OCC::ClientStatusReportingStatus::E2EeError_GeneralError);
return {};
}

Check warning on line 784 in src/libsync/foldermetadata.cpp

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Unmodified variable "end" of type "class QList<struct OCC::FolderMetadata::EncryptedFile>::const_iterator" should be const-qualified.

See more on https://sonarcloud.io/project/issues?id=nextcloud_desktop&issues=AZ7_FsAliYrk9yL2a3hy&open=AZ7_FsAliYrk9yL2a3hy&pullRequest=10238

QJsonObject encrypted;
encrypted.insert("key", QString(it->encryptionKey.toBase64()));
encrypted.insert("filename", it->originalFilename);
Expand All @@ -735,7 +795,7 @@
qCWarning(lcCseMetadata) << "Metadata generation failed!";
_account->reportClientStatus(OCC::ClientStatusReportingStatus::E2EeError_GeneralError);
}
QJsonObject file;

Check warning on line 798 in src/libsync/foldermetadata.cpp

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Unmodified variable "encryptedEncrypted" of type "class QString" should be const-qualified.

See more on https://sonarcloud.io/project/issues?id=nextcloud_desktop&issues=AZ7_FsAliYrk9yL2a3hz&open=AZ7_FsAliYrk9yL2a3hz&pullRequest=10238
file.insert(encryptedKey, encryptedEncrypted);
file.insert(initializationVectorKey, QString(it->initializationVector.toBase64()));
file.insert(authenticationTagKey, QString(it->authenticationTag.toBase64()));
Expand All @@ -749,7 +809,7 @@
}

auto metaObject = QJsonObject{
{metadataJsonKey, metadata},

Check warning on line 812 in src/libsync/foldermetadata.cpp

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Unmodified variable "end" of type "class QJsonObject::const_iterator" should be const-qualified.

See more on https://sonarcloud.io/project/issues?id=nextcloud_desktop&issues=AZ7_FsAliYrk9yL2a3h0&open=AZ7_FsAliYrk9yL2a3h0&pullRequest=10238
};

if (files.count()) {
Expand All @@ -772,12 +832,12 @@
return FolderMetadata::fromMedataVersionToItemEncryptionStatus(_existingMetadataVersion);
}

EncryptionStatusEnums::ItemEncryptionStatus FolderMetadata::encryptedMetadataEncryptionStatus() const

Check warning on line 835 in src/libsync/foldermetadata.cpp

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Rename this identifier to be shorter or equal to 31 characters.

See more on https://sonarcloud.io/project/issues?id=nextcloud_desktop&issues=AZ7_FsAliYrk9yL2a3h1&open=AZ7_FsAliYrk9yL2a3h1&pullRequest=10238
{
return FolderMetadata::fromMedataVersionToItemEncryptionStatus(_encryptedMetadataVersion);
}

bool FolderMetadata::isVersion2AndUp() const

Check warning on line 840 in src/libsync/foldermetadata.cpp

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Rename this identifier to be shorter or equal to 31 characters.

See more on https://sonarcloud.io/project/issues?id=nextcloud_desktop&issues=AZ7_FsAliYrk9yL2a3h2&open=AZ7_FsAliYrk9yL2a3h2&pullRequest=10238
{
return _existingMetadataVersion >= MetadataVersion::Version2_0;
}
Expand Down Expand Up @@ -814,7 +874,7 @@
_account->reportClientStatus(OCC::ClientStatusReportingStatus::E2EeError_GeneralError);
return false;
}
fileDropEntry.currentUser = fileDropEntryUser;

Check failure on line 877 in src/libsync/foldermetadata.cpp

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Refactor this code to not nest more than 3 if|for|do|while|switch statements.

See more on https://sonarcloud.io/project/issues?id=nextcloud_desktop&issues=AZ7_FsAliYrk9yL2a3h4&open=AZ7_FsAliYrk9yL2a3h4&pullRequest=10238
break;
}
}
Expand Down Expand Up @@ -864,9 +924,9 @@
switch (metadataVersion) {
case FolderMetadata::MetadataVersion::Version2_1:
case FolderMetadata::MetadataVersion::Version2_0:
return SyncFileItem::EncryptionStatus::EncryptedMigratedV2_0;

Check warning on line 927 in src/libsync/foldermetadata.cpp

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Rename this identifier to be shorter or equal to 31 characters.

See more on https://sonarcloud.io/project/issues?id=nextcloud_desktop&issues=AZ7_FsAliYrk9yL2a3h5&open=AZ7_FsAliYrk9yL2a3h5&pullRequest=10238
case FolderMetadata::MetadataVersion::Version1_2:
return SyncFileItem::EncryptionStatus::EncryptedMigratedV1_2;

Check failure on line 929 in src/libsync/foldermetadata.cpp

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Add a "default" case to this switch statement.

See more on https://sonarcloud.io/project/issues?id=nextcloud_desktop&issues=AZ7_FsAliYrk9yL2a3h6&open=AZ7_FsAliYrk9yL2a3h6&pullRequest=10238
case FolderMetadata::MetadataVersion::Version1:
return SyncFileItem::EncryptionStatus::Encrypted;
case FolderMetadata::MetadataVersion::VersionUndefined:
Expand All @@ -882,7 +942,7 @@
return MetadataVersion::Version1;
case EncryptionStatusEnums::ItemEncryptionStatus::EncryptedMigratedV1_2:
return MetadataVersion::Version1_2;
case EncryptionStatusEnums::ItemEncryptionStatus::EncryptedMigratedV2_0:

Check failure on line 945 in src/libsync/foldermetadata.cpp

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Add a "default" case to this switch statement.

See more on https://sonarcloud.io/project/issues?id=nextcloud_desktop&issues=AZ7_FsAliYrk9yL2a3h8&open=AZ7_FsAliYrk9yL2a3h8&pullRequest=10238
return MetadataVersion::Version2_0;
case EncryptionStatusEnums::ItemEncryptionStatus::NotEncrypted:
return MetadataVersion::VersionUndefined;
Expand Down Expand Up @@ -914,11 +974,17 @@
return metdataModified.toJson(QJsonDocument::Compact);
}

void FolderMetadata::addEncryptedFile(const EncryptedFile &f) {
bool FolderMetadata::addEncryptedFile(const EncryptedFile &f) {
Q_ASSERT(_isMetadataValid);
if (!_isMetadataValid) {
qCWarning(lcCseMetadata()) << "Could not add encrypted file to non-initialized metadata!";
return;
return false;
}

if (!isOriginalFilenameValid(f.originalFilename)) {
qCWarning(lcCseMetadata()) << "Could not add encrypted file with invalid original file name.";
_account->reportClientStatus(OCC::ClientStatusReportingStatus::E2EeError_GeneralError);
return false;
}

for (int i = 0; i < _files.size(); ++i) {
Expand All @@ -929,6 +995,7 @@
}

_files.append(f);
return true;
}

const QByteArray FolderMetadata::binaryMetadataKeyForDecryption() const
Expand All @@ -936,7 +1003,7 @@
return _binaryMetadataKeyForDecryption;
}

void FolderMetadata::removeEncryptedFile(const EncryptedFile &f)

Check warning on line 1006 in src/libsync/foldermetadata.cpp

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Remove this "const" qualifier from the return type in all declarations.

See more on https://sonarcloud.io/project/issues?id=nextcloud_desktop&issues=AZ7_FsAliYrk9yL2a3h9&open=AZ7_FsAliYrk9yL2a3h9&pullRequest=10238
{
for (int i = 0; i < _files.size(); ++i) {
if (_files.at(i).originalFilename == f.originalFilename) {
Expand Down Expand Up @@ -1001,7 +1068,9 @@
_account->reportClientStatus(OCC::ClientStatusReportingStatus::E2EeError_GeneralError);
return false;
}
addEncryptedFile(parsedEncryptedFile);
if (!addEncryptedFile(parsedEncryptedFile)) {
return false;
}
}

_fileDropEntries.clear();
Expand All @@ -1027,9 +1096,9 @@
Q_UNUSED(message);
if (statusCode != 200) {
qCWarning(lcCseMetadata()) << "Could not fetch root folder metadata" << statusCode << message;
_account->reportClientStatus(OCC::ClientStatusReportingStatus::E2EeError_GeneralError);

Check warning on line 1099 in src/libsync/foldermetadata.cpp

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Unmodified variable "statusCode" of type "int" should be const-qualified.

See more on https://sonarcloud.io/project/issues?id=nextcloud_desktop&issues=AZ7_FsAliYrk9yL2a3iA&open=AZ7_FsAliYrk9yL2a3iA&pullRequest=10238
}
const auto rootE2eeFolderMetadata = _encryptedFolderMetadataHandler->folderMetadata();

Check warning on line 1101 in src/libsync/foldermetadata.cpp

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Modify the macro definition so that it needs to be followed by a semicolon, or remove this empty statement.

See more on https://sonarcloud.io/project/issues?id=nextcloud_desktop&issues=AZ7_FsAliYrk9yL2a3iB&open=AZ7_FsAliYrk9yL2a3iB&pullRequest=10238
if (statusCode != 200 || !rootE2eeFolderMetadata->isValid() || !rootE2eeFolderMetadata->isVersion2AndUp()) {
initMetadata();
return;
Expand All @@ -1055,7 +1124,7 @@
Q_ASSERT(_isRootEncryptedFolder);
Q_ASSERT(!certificate.isNull());
if (!_isRootEncryptedFolder) {
qCWarning(lcCseMetadata()) << "Could not add a folder user to a non top level folder.";

Check warning on line 1127 in src/libsync/foldermetadata.cpp

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Unmodified variable "certificateType" of type "enum OCC::FolderMetadata::CertificateType" should be const-qualified.

See more on https://sonarcloud.io/project/issues?id=nextcloud_desktop&issues=AZ7_FsAliYrk9yL2a3iC&open=AZ7_FsAliYrk9yL2a3iC&pullRequest=10238
return false;
}

Expand All @@ -1065,7 +1134,7 @@
case CertificateType::HardwareCertificate:
convertedCertificateType = CertificateInformation::CertificateType::HardwareCertificate;
break;
case CertificateType::SoftwareNextcloudCertificate:

Check failure on line 1137 in src/libsync/foldermetadata.cpp

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Add a "default" case to this switch statement.

See more on https://sonarcloud.io/project/issues?id=nextcloud_desktop&issues=AZ7_FsAliYrk9yL2a3iD&open=AZ7_FsAliYrk9yL2a3iD&pullRequest=10238
convertedCertificateType = CertificateInformation::CertificateType::SoftwareNextcloudCertificate;
break;
}
Expand Down Expand Up @@ -1131,7 +1200,7 @@
const auto encryptedMetadataKey = encryptDataWithPublicKey(binaryMetadataKeyForEncryption(), shareUserCertificate);
if (encryptedMetadataKey.isEmpty()) {
qCWarning(lcCseMetadata()) << "Could not update folder users with empty encryptedMetadataKey!";
continue;

Check warning on line 1203 in src/libsync/foldermetadata.cpp

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Replace the redundant type with "auto".

See more on https://sonarcloud.io/project/issues?id=nextcloud_desktop&issues=AZ7_FsAliYrk9yL2a3iE&open=AZ7_FsAliYrk9yL2a3iE&pullRequest=10238

Check warning on line 1203 in src/libsync/foldermetadata.cpp

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Unmodified variable "shareUserCertificate" of type "class OCC::CertificateInformation" should be const-qualified.

See more on https://sonarcloud.io/project/issues?id=nextcloud_desktop&issues=AZ7_FsAliYrk9yL2a3iF&open=AZ7_FsAliYrk9yL2a3iF&pullRequest=10238
}

folderUser.encryptedMetadataKey = encryptedMetadataKey;
Expand All @@ -1145,7 +1214,7 @@
if (!_isRootEncryptedFolder) {
return;
}
_binaryMetadataKeyForEncryption = EncryptionHelper::generateRandom(metadataKeySize);

Check warning on line 1217 in src/libsync/foldermetadata.cpp

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Rename this identifier to be shorter or equal to 31 characters.

See more on https://sonarcloud.io/project/issues?id=nextcloud_desktop&issues=AZ7_FsAliYrk9yL2a3iG&open=AZ7_FsAliYrk9yL2a3iG&pullRequest=10238
if (!binaryMetadataKeyForEncryption().isEmpty()) {
_keyChecksums.insert(calcSha256(binaryMetadataKeyForEncryption()));
}
Expand Down
4 changes: 3 additions & 1 deletion src/libsync/foldermetadata.h
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
#pragma once
/*
* SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: GPL-2.0-or-later
*/

Check warning on line 5 in src/libsync/foldermetadata.h

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Edit this comment to use the C++ format, i.e. "//".

See more on https://sonarcloud.io/project/issues?id=nextcloud_desktop&issues=AZ7_FsDziYrk9yL2a3iH&open=AZ7_FsDziYrk9yL2a3iH&pullRequest=10238

#include "accountfwd.h"
#include "encryptedfoldermetadatahandler.h"
Expand All @@ -24,7 +24,7 @@
namespace OCC
{
// Handles parsing and altering the metadata, encryption and decryption. Setup of the instance is always asynchronouse and emits void setupComplete()
class OWNCLOUDSYNC_EXPORT FolderMetadata : public QObject

Check warning on line 27 in src/libsync/foldermetadata.h

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Class has 60 methods, which is greater than the 35 authorized. Split it into smaller classes.

See more on https://sonarcloud.io/project/issues?id=nextcloud_desktop&issues=AZ7_FsDziYrk9yL2a3iK&open=AZ7_FsDziYrk9yL2a3iK&pullRequest=10238
{
friend class ::TestClientSideEncryptionV2;
friend class ::TestSecureFileDrop;
Expand Down Expand Up @@ -93,11 +93,11 @@
Q_ENUM(MetadataVersion)

FolderMetadata(AccountPtr account, const QString &remoteFolderRoot, FolderType folderType = FolderType::Nested);
/*
* construct metadata based on RootEncryptedFolderInfo
* as per E2EE V2, the encryption key and users that have access are only stored in root(top-level) encrypted folder's metadata
* see: https://github.com/nextcloud/end_to_end_encryption_rfc/blob/v2.1/RFC.md
*/

Check warning on line 100 in src/libsync/foldermetadata.h

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Edit this comment to use the C++ format, i.e. "//".

See more on https://sonarcloud.io/project/issues?id=nextcloud_desktop&issues=AZ7_FsDziYrk9yL2a3iI&open=AZ7_FsDziYrk9yL2a3iI&pullRequest=10238
FolderMetadata(AccountPtr account,
const QString &remoteFolderRoot,
const QByteArray &metadata,
Expand Down Expand Up @@ -131,8 +131,8 @@
[[nodiscard]] QByteArray encryptedMetadata();

[[nodiscard]] EncryptionStatusEnums::ItemEncryptionStatus existingMetadataEncryptionStatus() const;
[[nodiscard]] EncryptionStatusEnums::ItemEncryptionStatus encryptedMetadataEncryptionStatus() const;

Check warning on line 134 in src/libsync/foldermetadata.h

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Rename this identifier to be shorter or equal to 31 characters.

See more on https://sonarcloud.io/project/issues?id=nextcloud_desktop&issues=AZ7_FsDziYrk9yL2a3iL&open=AZ7_FsDziYrk9yL2a3iL&pullRequest=10238

Check warning on line 135 in src/libsync/foldermetadata.h

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Rename this identifier to be shorter or equal to 31 characters.

See more on https://sonarcloud.io/project/issues?id=nextcloud_desktop&issues=AZ7_FsDziYrk9yL2a3iM&open=AZ7_FsDziYrk9yL2a3iM&pullRequest=10238
[[nodiscard]] bool isVersion2AndUp() const;

[[nodiscard]] quint64 newCounter() const;
Expand All @@ -144,9 +144,9 @@
void updateSelfCertificate();

static MetadataVersion setupVersionFromExistingMetadata(const QByteArray &metadata);

Check warning on line 147 in src/libsync/foldermetadata.h

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Rename this identifier to be shorter or equal to 31 characters.

See more on https://sonarcloud.io/project/issues?id=nextcloud_desktop&issues=AZ7_FsDziYrk9yL2a3iN&open=AZ7_FsDziYrk9yL2a3iN&pullRequest=10238
public slots:
void addEncryptedFile(const OCC::FolderMetadata::EncryptedFile &f);
[[nodiscard]] bool addEncryptedFile(const OCC::FolderMetadata::EncryptedFile &f);
void removeEncryptedFile(const OCC::FolderMetadata::EncryptedFile &f);
void removeAllEncryptedFiles();

Expand All @@ -171,6 +171,8 @@

[[nodiscard]] QJsonObject convertFileToJsonObject(const EncryptedFile *encryptedFile) const;

[[nodiscard]] static bool isOriginalFilenameValid(const QString &originalFilename);

[[nodiscard]] MetadataVersion latestSupportedMetadataVersion() const;

[[nodiscard]] bool parseFileDropPart(const QJsonDocument &doc);
Expand All @@ -178,8 +180,8 @@
void setFileDrop(const QJsonObject &fileDrop);

static EncryptionStatusEnums::ItemEncryptionStatus fromMedataVersionToItemEncryptionStatus(const MetadataVersion metadataVersion);
static MetadataVersion fromItemEncryptionStatusToMedataVersion(const EncryptionStatusEnums::ItemEncryptionStatus encryptionStatus);

Check warning on line 183 in src/libsync/foldermetadata.h

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Rename this identifier to be shorter or equal to 31 characters.

See more on https://sonarcloud.io/project/issues?id=nextcloud_desktop&issues=AZ7_FsDziYrk9yL2a3iO&open=AZ7_FsDziYrk9yL2a3iO&pullRequest=10238

Check warning on line 184 in src/libsync/foldermetadata.h

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Rename this identifier to be shorter or equal to 31 characters.

See more on https://sonarcloud.io/project/issues?id=nextcloud_desktop&issues=AZ7_FsDziYrk9yL2a3iP&open=AZ7_FsDziYrk9yL2a3iP&pullRequest=10238
static QByteArray prepareMetadataForSignature(const QJsonDocument &fullMetadata);

private slots:
Expand All @@ -192,10 +194,10 @@

void startFetchRootE2eeFolderMetadata(const QString &path);
void slotRootE2eeFolderMetadataReceived(int statusCode, const QString &message);

Check warning on line 197 in src/libsync/foldermetadata.h

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Rename this identifier to be shorter or equal to 31 characters.

See more on https://sonarcloud.io/project/issues?id=nextcloud_desktop&issues=AZ7_FsDziYrk9yL2a3iR&open=AZ7_FsDziYrk9yL2a3iR&pullRequest=10238
void updateUsersEncryptedMetadataKey();
void createNewMetadataKeyForEncryption();

Check warning on line 200 in src/libsync/foldermetadata.h

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Rename this identifier to be shorter or equal to 31 characters.

See more on https://sonarcloud.io/project/issues?id=nextcloud_desktop&issues=AZ7_FsDziYrk9yL2a3iS&open=AZ7_FsDziYrk9yL2a3iS&pullRequest=10238
void emitSetupComplete();

signals:
Expand Down
6 changes: 5 additions & 1 deletion src/libsync/propagateuploadencrypted.cpp
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/*
* SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: GPL-2.0-or-later
*/

Check warning on line 4 in src/libsync/propagateuploadencrypted.cpp

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Edit this comment to use the C++ format, i.e. "//".

See more on https://sonarcloud.io/project/issues?id=nextcloud_desktop&issues=AZ7_Fr6ZiYrk9yL2a3hS&open=AZ7_Fr6ZiYrk9yL2a3hS&pullRequest=10238
#include "propagateuploadencrypted.h"
#include "clientsideencryptionjobs.h"
#include "networkjobs.h"
Expand Down Expand Up @@ -79,12 +79,12 @@
return _encryptedFolderMetadataHandler->isFolderLocked();
}

const QByteArray PropagateUploadEncrypted::folderToken() const

Check warning on line 82 in src/libsync/propagateuploadencrypted.cpp

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Remove this "const" qualifier from the return type in all declarations.

See more on https://sonarcloud.io/project/issues?id=nextcloud_desktop&issues=AZ7_Fr6ZiYrk9yL2a3hT&open=AZ7_Fr6ZiYrk9yL2a3hT&pullRequest=10238
{
return _encryptedFolderMetadataHandler ? _encryptedFolderMetadataHandler->folderToken() : QByteArray{};
}

void PropagateUploadEncrypted::slotFetchMetadataJobFinished(int statusCode, const QString &message)

Check warning on line 87 in src/libsync/propagateuploadencrypted.cpp

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Unmodified variable "statusCode" of type "int" should be const-qualified.

See more on https://sonarcloud.io/project/issues?id=nextcloud_desktop&issues=AZ7_Fr6ZiYrk9yL2a3hU&open=AZ7_Fr6ZiYrk9yL2a3hU&pullRequest=10238
{
qCDebug(lcPropagateUploadEncrypted) << "Metadata Received, Preparing it for the new file." << message;

Expand Down Expand Up @@ -149,7 +149,7 @@
QFile output(QDir::tempPath() + QDir::separator() + encryptedFile.encryptedFilename);

QByteArray tag;
bool encryptionResult = EncryptionHelper::fileEncryption(encryptedFile.encryptionKey, encryptedFile.initializationVector, &input, &output, tag);

Check warning on line 152 in src/libsync/propagateuploadencrypted.cpp

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Unmodified variable "encryptionResult" of type "_Bool" should be const-qualified.

See more on https://sonarcloud.io/project/issues?id=nextcloud_desktop&issues=AZ7_Fr6ZiYrk9yL2a3hV&open=AZ7_Fr6ZiYrk9yL2a3hV&pullRequest=10238

if (!encryptionResult) {
qCWarning(lcPropagateUploadEncrypted()) << "There was an error encrypting the file, aborting upload.";
Expand All @@ -163,7 +163,11 @@

qCDebug(lcPropagateUploadEncrypted) << "Creating the metadata for the encrypted file.";

metadata->addEncryptedFile(encryptedFile);
if (!metadata->addEncryptedFile(encryptedFile)) {
qCWarning(lcPropagateUploadEncrypted()) << "There was an error encrypting the file, aborting upload. Invalid metadata file name.";
emit error();

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Finish encrypted mkdir on invalid names

When this new rejection is reached for an encrypted directory upload, such as a POSIX folder named foo\bar inside an E2EE parent, the only consumer in PropagateRemoteMkdir::slotMkdir() connects PropagateUploadEncrypted::error to a logging-only lambda (src/libsync/propagateremotemkdir.cpp:212), unlike file uploads which call done(). Emitting error() here therefore leaves the mkdir job in _activeJobList with no MKCOL or completion signal, so the sync can stall instead of failing the item; make the mkdir path finish/cleanup on this failure.

Useful? React with 👍 / 👎.

return;
}

qCDebug(lcPropagateUploadEncrypted) << "Metadata created, sending to the server.";

Expand Down
82 changes: 79 additions & 3 deletions test/testclientsideencryptionv2.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ private slots:
encryptedFile.originalFilename = fakeFileName;
encryptedFile.mimetype = "application/octet-stream";
encryptedFile.initializationVector = EncryptionHelper::generateRandom(16);
metadata->addEncryptedFile(encryptedFile);
QVERIFY(metadata->addEncryptedFile(encryptedFile));

const auto encryptedMetadata = metadata->encryptedMetadata();
QVERIFY(!encryptedMetadata.isEmpty());
Expand Down Expand Up @@ -219,6 +219,82 @@ private slots:
QVERIFY(!metadataFromJson->isValid());
}

void testOriginalFilenameValidation_data()
{
QTest::addColumn<QString>("originalFilename");
QTest::addColumn<bool>("isValid");

QTest::newRow("plain file") << QStringLiteral("document.txt") << true;
QTest::newRow("plain folder") << QStringLiteral("Documents") << true;
QTest::newRow("hidden file") << QStringLiteral(".hidden") << true;
QTest::newRow("empty") << QString() << false;
QTest::newRow("current directory") << QStringLiteral(".") << false;
QTest::newRow("parent directory") << QStringLiteral("..") << false;
QTest::newRow("relative traversal") << QStringLiteral("../../poc_dir") << false;
QTest::newRow("embedded slash") << QStringLiteral("folder/file.txt") << false;
QTest::newRow("absolute path") << QStringLiteral("/tmp/poc") << false;
QTest::newRow("backslash") << QStringLiteral("folder\\file.txt") << false;
QTest::newRow("null byte") << QStringLiteral("file") + QChar(0) + QStringLiteral("name") << false;
}

void testOriginalFilenameValidation()
{
QFETCH(QString, originalFilename);
QFETCH(bool, isValid);

QCOMPARE(FolderMetadata::isOriginalFilenameValid(originalFilename), isValid);
}

void testParseEncryptedFileFromJsonRejectsUnsafeOriginalFilename_data()
{
testOriginalFilenameValidation_data();
}

void testParseEncryptedFileFromJsonRejectsUnsafeOriginalFilename()
{
QFETCH(QString, originalFilename);
QFETCH(bool, isValid);

QScopedPointer<FolderMetadata> metadata(new FolderMetadata(_account, "/", FolderMetadata::FolderType::Root));
QSignalSpy metadataSetupCompleteSpy(metadata.data(), &FolderMetadata::setupComplete);
metadataSetupCompleteSpy.wait();
QCOMPARE(metadataSetupCompleteSpy.count(), 1);
QVERIFY(metadata->isValid());

const auto fileJson = QJsonObject{
{QStringLiteral("filename"), originalFilename},
{QStringLiteral("key"), QString::fromUtf8(QByteArrayLiteral("key").toBase64())},
{QStringLiteral("mimetype"), QStringLiteral("application/octet-stream")},
{QStringLiteral("nonce"), QString::fromUtf8(QByteArrayLiteral("nonce").toBase64())},
{QStringLiteral("authenticationTag"), QString::fromUtf8(QByteArrayLiteral("tag").toBase64())},
};

const auto parsedEncryptedFile = metadata->parseEncryptedFileFromJson(QStringLiteral("encrypted-name"), fileJson);
QCOMPARE(!parsedEncryptedFile.originalFilename.isEmpty(), isValid);
if (isValid) {
QCOMPARE(parsedEncryptedFile.originalFilename, originalFilename);
}
}

void testAddEncryptedFileRejectsUnsafeOriginalFilename()
{
QScopedPointer<FolderMetadata> metadata(new FolderMetadata(_account, "/", FolderMetadata::FolderType::Root));
QSignalSpy metadataSetupCompleteSpy(metadata.data(), &FolderMetadata::setupComplete);
metadataSetupCompleteSpy.wait();
QCOMPARE(metadataSetupCompleteSpy.count(), 1);
QVERIFY(metadata->isValid());

FolderMetadata::EncryptedFile encryptedFile;
encryptedFile.encryptionKey = EncryptionHelper::generateRandom(16);
encryptedFile.encryptedFilename = EncryptionHelper::generateRandomFilename();
encryptedFile.originalFilename = QStringLiteral("folder\\file.txt");
encryptedFile.mimetype = "application/octet-stream";
encryptedFile.initializationVector = EncryptionHelper::generateRandom(16);

QVERIFY(!metadata->addEncryptedFile(encryptedFile));
QVERIFY(metadata->files().isEmpty());
}

void testE2EeFolderMetadataSharing()
{
// instantiate empty metadata, add a file, and share with a second user "sharee"
Expand All @@ -236,7 +312,7 @@ private slots:
encryptedFile.originalFilename = fakeFileName;
encryptedFile.mimetype = "application/octet-stream";
encryptedFile.initializationVector = EncryptionHelper::generateRandom(16);
metadata->addEncryptedFile(encryptedFile);
QVERIFY(metadata->addEncryptedFile(encryptedFile));

QVERIFY(metadata->addUser(_secondAccount->davUser(), _secondAccount->e2e()->getCertificate(), FolderMetadata::CertificateType::SoftwareNextcloudCertificate));

Expand Down Expand Up @@ -327,7 +403,7 @@ private slots:
encryptedFile.originalFilename = fakeFileNameFromSecondUser;
encryptedFile.mimetype = "application/octet-stream";
encryptedFile.initializationVector = EncryptionHelper::generateRandom(16);
metadataFromJsonForSecondUser->addEncryptedFile(encryptedFile);
QVERIFY(metadataFromJsonForSecondUser->addEncryptedFile(encryptedFile));

auto encryptedMetadataFromSecondUser = metadataFromJsonForSecondUser->encryptedMetadata();
encryptedMetadataFromSecondUser.replace("\"", "\\\"");
Expand Down
2 changes: 1 addition & 1 deletion test/testsecurefiledrop.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ private slots:
encryptedFile.originalFilename = fakeFileName;
encryptedFile.mimetype = "application/octet-stream";
encryptedFile.initializationVector = EncryptionHelper::generateRandom(16);
metadata->addEncryptedFile(encryptedFile);
QVERIFY(metadata->addEncryptedFile(encryptedFile));
}

QJsonObject fakeFileDropPart;
Expand Down
Loading