File indexing completed on 2025-01-26 04:57:20

0001 /*
0002    SPDX-FileCopyrightText: 2016-2024 Laurent Montel <montel@kde.org>
0003 
0004    SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #include "createdatabasefilejobtest.h"
0008 #include "../createdatabasefilejob.h"
0009 #include "../createphishingurldatabasejob.h"
0010 #include "../localdatabasefile.h"
0011 #include <QStandardPaths>
0012 
0013 #include <QDebug>
0014 #include <QSignalSpy>
0015 #include <QTest>
0016 Q_DECLARE_METATYPE(QList<WebEngineViewer::Addition>)
0017 QByteArray readJsonFile(const QString &jsonFile)
0018 {
0019     QFile file(QLatin1StringView(CHECKPHISHINGURL_DATA_DIR) + QLatin1Char('/') + jsonFile);
0020     file.open(QIODevice::ReadOnly);
0021     Q_ASSERT(file.isOpen());
0022     const QByteArray data = file.readAll();
0023     Q_ASSERT(!data.isEmpty());
0024     return data;
0025 }
0026 
0027 CreateDatabaseFileJobTest::CreateDatabaseFileJobTest(QObject *parent)
0028     : QObject(parent)
0029 {
0030     QStandardPaths::setTestModeEnabled(true);
0031     QDir().mkpath(QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QStringLiteral("/phishingurl"));
0032 }
0033 
0034 CreateDatabaseFileJobTest::~CreateDatabaseFileJobTest() = default;
0035 
0036 void CreateDatabaseFileJobTest::initTestCase()
0037 {
0038     qRegisterMetaType<WebEngineViewer::CreatePhishingUrlDataBaseJob::DataBaseDownloadResult>();
0039     qRegisterMetaType<WebEngineViewer::CreatePhishingUrlDataBaseJob::ContraintsCompressionType>();
0040     qRegisterMetaType<WebEngineViewer::UpdateDataBaseInfo>();
0041 }
0042 
0043 void CreateDatabaseFileJobTest::shouldHaveDefaultValue()
0044 {
0045     WebEngineViewer::CreateDatabaseFileJob job;
0046     QVERIFY(!job.canStart());
0047 }
0048 
0049 void CreateDatabaseFileJobTest::shouldCreateFile_data()
0050 {
0051     QTest::addColumn<QString>("filename");
0052     QTest::addColumn<quint64>("numberOfElement");
0053     QTest::addColumn<bool>("success");
0054     QTest::newRow("correctdatabase") << QStringLiteral("current.json") << static_cast<quint64>(580600) << true;
0055     QTest::newRow("correctdatabase2") << QStringLiteral("newdatabase2.json") << static_cast<quint64>(579416) << true;
0056     QTest::newRow("incorrectdatabase") << QStringLiteral("incorrectdatabase2.json") << static_cast<quint64>(0) << false;
0057 }
0058 
0059 void CreateDatabaseFileJobTest::shouldCreateFile()
0060 {
0061     QFETCH(QString, filename);
0062     QFETCH(quint64, numberOfElement);
0063     QFETCH(bool, success);
0064 
0065     const QByteArray ba = readJsonFile(filename);
0066     WebEngineViewer::CreatePhishingUrlDataBaseJob job;
0067     QSignalSpy spy1(&job, &WebEngineViewer::CreatePhishingUrlDataBaseJob::finished);
0068     job.parseResult(ba);
0069     QCOMPARE(spy1.count(), 1);
0070     const auto info = spy1.at(0).at(0).value<WebEngineViewer::UpdateDataBaseInfo>();
0071     WebEngineViewer::CreateDatabaseFileJob databasejob;
0072     const QString createDataBaseName =
0073         QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1StringView("/phishingurl") + QLatin1StringView("/test.db");
0074     qDebug() << " new filename " << createDataBaseName;
0075     databasejob.setFileName(createDataBaseName);
0076     databasejob.setUpdateDataBaseInfo(info);
0077 
0078     QSignalSpy spy2(&databasejob, &WebEngineViewer::CreateDatabaseFileJob::finished);
0079     databasejob.start();
0080     QCOMPARE(spy2.count(), 1);
0081     bool successCreateDataBase = spy2.at(0).at(0).toBool();
0082     QCOMPARE(successCreateDataBase, success);
0083 
0084     WebEngineViewer::LocalDataBaseFile newFile(createDataBaseName);
0085     QVERIFY(newFile.isValid());
0086     QCOMPARE(newFile.getUint16(0), static_cast<quint16>(1));
0087     QCOMPARE(newFile.getUint16(2), static_cast<quint16>(0));
0088     if (success) {
0089         QCOMPARE(newFile.getUint64(4), numberOfElement);
0090     }
0091 }
0092 
0093 void CreateDatabaseFileJobTest::shouldRemoveElementInDataBase_data()
0094 {
0095     QTest::addColumn<QList<quint32>>("listElementToRemove");
0096     QTest::addColumn<QList<WebEngineViewer::Addition>>("listElementToAdd");
0097     QTest::addColumn<QByteArray>("newssha");
0098     QTest::addColumn<bool>("success");
0099     QList<WebEngineViewer::Addition> lstAdditions;
0100     QList<quint32> r = {2, 3, 4};
0101     QTest::newRow("correctdatabase") << r << lstAdditions << QByteArrayLiteral("yTnyjAgIFeS6Cv+b4IJHngYbdvp5uz1bx9V4el5CyeE=") << true;
0102     r = {3, 2, 4};
0103     QTest::newRow("correctdatabaseotherorder") << r << lstAdditions << QByteArrayLiteral("yTnyjAgIFeS6Cv+b4IJHngYbdvp5uz1bx9V4el5CyeE=") << true;
0104 
0105     r = {4, 2, 3};
0106     QTest::newRow("correctdatabaseotherorder2") << r << lstAdditions << QByteArrayLiteral("yTnyjAgIFeS6Cv+b4IJHngYbdvp5uz1bx9V4el5CyeE=") << true;
0107 
0108     //    >>> import hashlib
0109     //    >>> m = hashlib.sha256()
0110     //    >>> m.update("111154321abcdabcdebbbbbcdef")
0111     //    >>> m.digest()
0112     //    '\x81\xdd9\xe3\xae\x94s\xfd\x16o\\\xcea \xb7\xbc\x1b+R\nN\x05o\xfe\xeeWY\x7f\x8a\xcb\xbeN'
0113     //    >>> import base64
0114     //    >>> encoded = base64.b64encode(m.digest())
0115     //    >>> encoded
0116     //    'gd05466Uc/0Wb1zOYSC3vBsrUgpOBW/+7ldZf4rLvk4='
0117     //    >>>
0118 
0119     r = {0, 2, 8};
0120     QTest::newRow("correctdatabaseotherorder3") << r << lstAdditions << QByteArrayLiteral("gd05466Uc/0Wb1zOYSC3vBsrUgpOBW/+7ldZf4rLvk4=") << true;
0121 
0122     r = {0, 2, 8};
0123 
0124     WebEngineViewer::Addition c;
0125     c.hashString = QByteArray("mnopqrst");
0126     c.prefixSize = 4;
0127     c.compressionType = WebEngineViewer::UpdateDataBaseInfo::RawCompression;
0128 
0129     WebEngineViewer::Addition b;
0130     b.hashString = QByteArray("uvwx");
0131     b.prefixSize = 4;
0132     b.compressionType = WebEngineViewer::UpdateDataBaseInfo::RawCompression;
0133 
0134     lstAdditions << c << b;
0135 
0136     //    >>> import hashlib
0137     //    >>> m = hashlib.sha256()
0138     //    >>> m.update("111154321abcdabcdebbbbbcdefmnopqrstuvwx")
0139     //    >>> m.digest()
0140     //    '\n\xae\xe2\xe0!\x8f\xa4\x05N\x89,\xdcJ*\xbe\x85\xa1Q\xc3\x9c\xc8}j\x83*s\xd5L&\xbe\xfbh'
0141     //    >>> import base64
0142     //    >>> encoded = base64.b64encode(m.digest())
0143     //    >>> encoded
0144     //    'Cq7i4CGPpAVOiSzcSiq+haFRw5zIfWqDKnPVTCa++2g='
0145 
0146     // m.update("111154321abcdabcdebbbbbcdefmnopqrstuvwx");
0147 
0148     QTest::newRow("correctdatabaseotherorderwithadditions") << r << lstAdditions << QByteArrayLiteral("Cq7i4CGPpAVOiSzcSiq+haFRw5zIfWqDKnPVTCa++2g=") << true;
0149 }
0150 
0151 void CreateDatabaseFileJobTest::shouldRemoveElementInDataBase()
0152 {
0153     QFETCH(QList<quint32>, listElementToRemove);
0154     QFETCH(QList<WebEngineViewer::Addition>, listElementToAdd);
0155     QFETCH(QByteArray, newssha);
0156     QFETCH(bool, success);
0157 
0158     // Proof of checksum validity using python:
0159     // >>> import hashlib
0160     // >>> m = hashlib.sha256()
0161     // >>> m.update("----11112222254321abcdabcdebbbbbcdefefgh")
0162     // >>> m.digest()
0163     // "\xbc\xb3\xedk\xe3x\xd1(\xa9\xedz7]"
0164     // "x\x18\xbdn]\xa5\xa8R\xf7\xab\xcf\xc1\xa3\xa3\xc5Z,\xa6o"
0165 
0166     WebEngineViewer::CreateDatabaseFileJob databasejob;
0167     const QString createDataBaseName =
0168         QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1StringView("/phishingurl") + QLatin1StringView("/correctBinary.db");
0169     qDebug() << " new filename " << createDataBaseName;
0170     databasejob.setFileName(createDataBaseName);
0171 
0172     WebEngineViewer::UpdateDataBaseInfo info;
0173 
0174     WebEngineViewer::Addition a;
0175     a.hashString = QByteArray("----1111bbbb");
0176     a.prefixSize = 4;
0177     a.compressionType = WebEngineViewer::UpdateDataBaseInfo::RawCompression;
0178 
0179     WebEngineViewer::Addition b;
0180     b.hashString = QByteArray("abcdefgh");
0181     b.prefixSize = 4;
0182     b.compressionType = WebEngineViewer::UpdateDataBaseInfo::RawCompression;
0183 
0184     WebEngineViewer::Addition c;
0185     c.hashString = QByteArray("54321abcde");
0186     c.prefixSize = 5;
0187     c.compressionType = WebEngineViewer::UpdateDataBaseInfo::RawCompression;
0188 
0189     WebEngineViewer::Addition d;
0190     d.hashString = QByteArray("22222bcdef");
0191     d.prefixSize = 5;
0192     d.compressionType = WebEngineViewer::UpdateDataBaseInfo::RawCompression;
0193 
0194     QList<WebEngineViewer::Addition> lst;
0195     lst << a << b << c << d;
0196     info.additionList = lst;
0197     info.minimumWaitDuration = QStringLiteral("593.440s");
0198     info.threatType = QStringLiteral("MALWARE");
0199     info.threatEntryType = QStringLiteral("URL");
0200     info.responseType = WebEngineViewer::UpdateDataBaseInfo::FullUpdate;
0201     info.platformType = QStringLiteral("WINDOWS");
0202     info.newClientState = QStringLiteral("ChAIBRADGAEiAzAwMSiAEDABEAFGpqhd");
0203     info.sha256 = QByteArrayLiteral("vLPta+N40Sip7Xo3XXgYvW5dpahS96vPwaOjxVospm8=");
0204 
0205     databasejob.setUpdateDataBaseInfo(info);
0206 
0207     QSignalSpy spy2(&databasejob, &WebEngineViewer::CreateDatabaseFileJob::finished);
0208     databasejob.start();
0209     QCOMPARE(spy2.count(), 1);
0210     bool successCreateDataBase = spy2.at(0).at(0).toBool();
0211     QVERIFY(successCreateDataBase);
0212 
0213     WebEngineViewer::LocalDataBaseFile newFile(createDataBaseName);
0214     QVERIFY(newFile.isValid());
0215     QCOMPARE(newFile.getUint16(0), static_cast<quint16>(1));
0216     QCOMPARE(newFile.getUint16(2), static_cast<quint16>(0));
0217     QCOMPARE(newFile.getUint64(4), static_cast<quint64>(9));
0218     int index = 4 + sizeof(quint64);
0219     QList<QByteArray> storageData;
0220     storageData << QByteArrayLiteral("----");
0221     storageData << QByteArrayLiteral("1111");
0222     storageData << QByteArrayLiteral("22222");
0223     storageData << QByteArrayLiteral("54321");
0224     storageData << QByteArrayLiteral("abcd");
0225     storageData << QByteArrayLiteral("abcde");
0226     storageData << QByteArrayLiteral("bbbb");
0227     storageData << QByteArrayLiteral("bcdef");
0228     storageData << QByteArrayLiteral("efgh");
0229 
0230     for (int i = 0; i < 9; ++i) {
0231         quint64 value = newFile.getUint64(index);
0232         // qDebug() << "char "<< newFile.getCharStar(value);
0233         QCOMPARE(storageData.at(i), QByteArray(newFile.getCharStar(value)));
0234         index += sizeof(quint64);
0235     }
0236     const QList<WebEngineViewer::Addition> lstInfo = newFile.extractAllInfo();
0237     QCOMPARE(lstInfo.count(), 9);
0238     for (int i = 0; i < 9; i++) {
0239         QCOMPARE(lstInfo.at(i).hashString, storageData.at(i));
0240         QCOMPARE(lstInfo.at(i).prefixSize, lstInfo.at(i).hashString.size());
0241     }
0242 
0243     // Before
0244     // storageData << QByteArrayLiteral("----");
0245     // storageData << QByteArrayLiteral("1111");
0246     // storageData << QByteArrayLiteral("22222");
0247     // storageData << QByteArrayLiteral("54321");
0248     // storageData << QByteArrayLiteral("abcd");
0249     // storageData << QByteArrayLiteral("abcde");
0250     // storageData << QByteArrayLiteral("bbbb");
0251     // storageData << QByteArrayLiteral("bcdef");
0252     // storageData << QByteArrayLiteral("efgh");
0253 
0254     // TODO remove items.
0255     WebEngineViewer::UpdateDataBaseInfo updateinfo;
0256 
0257     // we will remove QByteArrayLiteral("22222"); QByteArrayLiteral("54321"); QByteArrayLiteral("abcd");
0258     WebEngineViewer::Removal r;
0259     r.indexes = listElementToRemove;
0260     r.compressionType = WebEngineViewer::UpdateDataBaseInfo::RawCompression;
0261 
0262     // Proof of checksum validity using python:
0263     // >>> import hashlib
0264     // >>> m = hashlib.sha256()
0265     // >>> m.update("----1111abcdebbbbbcdefefgh")
0266     // >>> m.digest()
0267     // '\xc99\xf2\x8c\x08\x08\x15\xe4\xba\n\xff\x9b\xe0\x82G\x9e\x06\x1bv\xfay\xbb=[\xc7\xd5xz^B\xc9\xe1'
0268     // >>> import base64
0269     // >>> encoded = base64.b64encode(m.digest())
0270     // >>> encoded
0271     // 'yTnyjAgIFeS6Cv+b4IJHngYbdvp5uz1bx9V4el5CyeE='
0272 
0273     QList<WebEngineViewer::Removal> lstRemovals;
0274     lstRemovals << r;
0275     updateinfo.additionList = listElementToAdd;
0276     updateinfo.removalList = lstRemovals;
0277     updateinfo.minimumWaitDuration = QStringLiteral("593.440s");
0278     updateinfo.threatType = QStringLiteral("MALWARE");
0279     updateinfo.threatEntryType = QStringLiteral("URL");
0280     updateinfo.responseType = WebEngineViewer::UpdateDataBaseInfo::PartialUpdate;
0281     updateinfo.platformType = QStringLiteral("WINDOWS");
0282     updateinfo.newClientState = QStringLiteral("ChAIBRADGAEiAzAwMSiAEDABEAFGpqhd");
0283     updateinfo.sha256 = /*QByteArrayLiteral("yTnyjAgIFeS6Cv+b4IJHngYbdvp5uz1bx9V4el5CyeE=")*/ newssha;
0284 
0285     WebEngineViewer::CreateDatabaseFileJob updateDatabasejob;
0286     qDebug() << " new filename " << createDataBaseName;
0287     updateDatabasejob.setFileName(createDataBaseName);
0288 
0289     updateDatabasejob.setUpdateDataBaseInfo(updateinfo);
0290 
0291     QSignalSpy spy3(&updateDatabasejob, &WebEngineViewer::CreateDatabaseFileJob::finished);
0292     updateDatabasejob.start();
0293     QCOMPARE(spy3.count(), 1);
0294     successCreateDataBase = spy3.at(0).at(0).toBool();
0295     QCOMPARE(successCreateDataBase, success);
0296 }
0297 
0298 void CreateDatabaseFileJobTest::shouldCreateCorrectBinaryFile()
0299 {
0300     WebEngineViewer::CreateDatabaseFileJob databasejob;
0301     const QString createDataBaseName =
0302         QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1StringView("/phishingurl") + QLatin1StringView("/correctBinary.db");
0303     qDebug() << " new filename " << createDataBaseName;
0304     databasejob.setFileName(createDataBaseName);
0305 
0306     WebEngineViewer::UpdateDataBaseInfo info;
0307 
0308     WebEngineViewer::Addition a;
0309     a.hashString = QByteArray("----1111bbbb");
0310     a.prefixSize = 4;
0311     a.compressionType = WebEngineViewer::UpdateDataBaseInfo::RawCompression;
0312 
0313     WebEngineViewer::Addition b;
0314     b.hashString = QByteArray("abcdefgh");
0315     b.prefixSize = 4;
0316     b.compressionType = WebEngineViewer::UpdateDataBaseInfo::RawCompression;
0317 
0318     WebEngineViewer::Addition c;
0319     c.hashString = QByteArray("54321abcde");
0320     c.prefixSize = 5;
0321     c.compressionType = WebEngineViewer::UpdateDataBaseInfo::RawCompression;
0322 
0323     WebEngineViewer::Addition d;
0324     d.hashString = QByteArray("22222bcdef");
0325     d.prefixSize = 5;
0326     d.compressionType = WebEngineViewer::UpdateDataBaseInfo::RawCompression;
0327 
0328     QList<WebEngineViewer::Addition> lst;
0329     lst << a << b << c << d;
0330     info.additionList = lst;
0331     info.minimumWaitDuration = QStringLiteral("593.440s");
0332     info.threatType = QStringLiteral("MALWARE");
0333     info.threatEntryType = QStringLiteral("URL");
0334     info.responseType = WebEngineViewer::UpdateDataBaseInfo::FullUpdate;
0335     info.platformType = QStringLiteral("WINDOWS");
0336     info.newClientState = QStringLiteral("ChAIBRADGAEiAzAwMSiAEDABEAFGpqhd");
0337     info.sha256 = QByteArrayLiteral("vLPta+N40Sip7Xo3XXgYvW5dpahS96vPwaOjxVospm8=");
0338 
0339     databasejob.setUpdateDataBaseInfo(info);
0340 
0341     QSignalSpy spy2(&databasejob, &WebEngineViewer::CreateDatabaseFileJob::finished);
0342     databasejob.start();
0343     QCOMPARE(spy2.count(), 1);
0344     bool successCreateDataBase = spy2.at(0).at(0).toBool();
0345     QVERIFY(successCreateDataBase);
0346 
0347     WebEngineViewer::LocalDataBaseFile newFile(createDataBaseName);
0348     QVERIFY(newFile.isValid());
0349     QCOMPARE(newFile.getUint16(0), static_cast<quint16>(1));
0350     QCOMPARE(newFile.getUint16(2), static_cast<quint16>(0));
0351     QCOMPARE(newFile.getUint64(4), static_cast<quint64>(9));
0352     int index = 4 + sizeof(quint64);
0353     QList<QByteArray> storageData;
0354     storageData << QByteArrayLiteral("----");
0355     storageData << QByteArrayLiteral("1111");
0356     storageData << QByteArrayLiteral("22222");
0357     storageData << QByteArrayLiteral("54321");
0358     storageData << QByteArrayLiteral("abcd");
0359     storageData << QByteArrayLiteral("abcde");
0360     storageData << QByteArrayLiteral("bbbb");
0361     storageData << QByteArrayLiteral("bcdef");
0362     storageData << QByteArrayLiteral("efgh");
0363 
0364     for (int i = 0; i < 9; ++i) {
0365         quint64 value = newFile.getUint64(index);
0366         // qDebug() << "char "<< newFile.getCharStar(value);
0367         QCOMPARE(storageData.at(i), QByteArray(newFile.getCharStar(value)));
0368         index += sizeof(quint64);
0369     }
0370     const QList<WebEngineViewer::Addition> lstInfo = newFile.extractAllInfo();
0371     QCOMPARE(lstInfo.count(), 9);
0372     for (int i = 0; i < 9; i++) {
0373         QCOMPARE(lstInfo.at(i).hashString, storageData.at(i));
0374         QCOMPARE(lstInfo.at(i).prefixSize, lstInfo.at(i).hashString.size());
0375     }
0376 }
0377 
0378 void CreateDatabaseFileJobTest::shouldUpdateDataBase()
0379 {
0380     QString firstFilename = QStringLiteral("newdatabase2.json");
0381     const QByteArray ba = readJsonFile(firstFilename);
0382     WebEngineViewer::CreatePhishingUrlDataBaseJob job;
0383     QSignalSpy spy1(&job, &WebEngineViewer::CreatePhishingUrlDataBaseJob::finished);
0384     job.parseResult(ba);
0385     QCOMPARE(spy1.count(), 1);
0386     const auto info = spy1.at(0).at(0).value<WebEngineViewer::UpdateDataBaseInfo>();
0387     WebEngineViewer::CreateDatabaseFileJob databasejob;
0388     const QString createDataBaseName =
0389         QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1StringView("/phishingurl") + QLatin1StringView("/update.db");
0390     // qDebug() << " new filename " << createDataBaseName;
0391     databasejob.setFileName(createDataBaseName);
0392     databasejob.setUpdateDataBaseInfo(info);
0393 
0394     QSignalSpy spy2(&databasejob, &WebEngineViewer::CreateDatabaseFileJob::finished);
0395     databasejob.start();
0396     QCOMPARE(spy2.count(), 1);
0397     bool successCreateDataBase = spy2.at(0).at(0).toBool();
0398     QVERIFY(successCreateDataBase);
0399 
0400     WebEngineViewer::LocalDataBaseFile newFile(createDataBaseName);
0401     QVERIFY(newFile.isValid());
0402     QCOMPARE(newFile.getUint16(0), static_cast<quint16>(1));
0403     QCOMPARE(newFile.getUint16(2), static_cast<quint16>(0));
0404     QCOMPARE(newFile.getUint64(4), static_cast<quint64>(579416));
0405     newFile.close();
0406 
0407     QString updateFilename = QStringLiteral("partial_download3.json");
0408     const QByteArray baUpdate = readJsonFile(updateFilename);
0409 
0410     WebEngineViewer::CreatePhishingUrlDataBaseJob jobUpdate;
0411     QSignalSpy spy3(&jobUpdate, &WebEngineViewer::CreatePhishingUrlDataBaseJob::finished);
0412     jobUpdate.parseResult(baUpdate);
0413     QCOMPARE(spy3.count(), 1);
0414 
0415     const auto infoUpdate = spy3.at(0).at(0).value<WebEngineViewer::UpdateDataBaseInfo>();
0416     QCOMPARE(infoUpdate.responseType, WebEngineViewer::UpdateDataBaseInfo::PartialUpdate);
0417 
0418     WebEngineViewer::CreateDatabaseFileJob databasejob2;
0419     databasejob2.setFileName(createDataBaseName);
0420     databasejob2.setUpdateDataBaseInfo(infoUpdate);
0421 
0422     QSignalSpy spy4(&databasejob2, &WebEngineViewer::CreateDatabaseFileJob::finished);
0423     databasejob2.start();
0424     QCOMPARE(spy4.count(), 1);
0425     successCreateDataBase = spy4.at(0).at(0).toBool();
0426     QEXPECT_FAIL("", "Expected a success but not", Continue);
0427     QVERIFY(successCreateDataBase);
0428 }
0429 
0430 QTEST_MAIN(CreateDatabaseFileJobTest)
0431 
0432 #include "moc_createdatabasefilejobtest.cpp"