19
#include "TestAutoType.h"
21
#include <QPluginLoader>
24
#include "autotype/AutoType.h"
25
#include "autotype/AutoTypePlatformPlugin.h"
26
#include "autotype/test/AutoTypeTestInterface.h"
27
#include "core/Config.h"
28
#include "core/Group.h"
29
#include "core/Resources.h"
30
#include "crypto/Crypto.h"
31
#include "gui/MessageBox.h"
32
#include "gui/osutils/OSUtils.h"
34
QTEST_GUILESS_MAIN(TestAutoType)
36
void TestAutoType::initTestCase()
38
QVERIFY(Crypto::init());
39
Config::createTempFileInstance();
40
config()->set(Config::AutoTypeDelay, 1);
41
config()->set(Config::Security_AutoTypeAsk, false);
42
AutoType::createTestInstance();
44
QPluginLoader loader(resources()->pluginPath("keepassxc-autotype-test"));
45
loader.setLoadHints(QLibrary::ResolveAllSymbolsHint);
46
QVERIFY(loader.instance());
48
m_platform = qobject_cast<AutoTypePlatformInterface*>(loader.instance());
51
m_test = qobject_cast<AutoTypeTestInterface*>(loader.instance());
54
m_autoType = AutoType::instance();
57
void TestAutoType::init()
59
config()->set(Config::AutoTypeEntryTitleMatch, false);
60
m_test->clearActions();
62
m_db = QSharedPointer<Database>::create();
64
m_dbList.append(m_db);
65
m_group = m_db->rootGroup();
67
AutoTypeAssociations::Association association;
69
m_entry1 = new Entry();
70
m_entry1->setGroup(m_group);
71
m_entry1->setUsername("myuser");
72
m_entry1->setPassword("mypass");
73
association.window = "custom window";
74
association.sequence = "{username}association{password}";
75
m_entry1->autoTypeAssociations()->add(association);
77
m_entry2 = new Entry();
78
m_entry2->setGroup(m_group);
79
m_entry2->setPassword("myuser");
80
m_entry2->setTitle("entry title");
82
m_entry3 = new Entry();
83
m_entry3->setGroup(m_group);
84
m_entry3->setPassword("regex");
85
association.window = "//REGEX1//";
86
association.sequence = "regex1";
87
m_entry3->autoTypeAssociations()->add(association);
88
association.window = "//^REGEX2$//";
89
association.sequence = "regex2";
90
m_entry3->autoTypeAssociations()->add(association);
91
association.window = "//^REGEX3-([rd]\\d){2}$//";
92
association.sequence = "regex3";
93
m_entry3->autoTypeAssociations()->add(association);
95
m_entry4 = new Entry();
96
m_entry4->setGroup(m_group);
97
m_entry4->setPassword("custom_attr");
98
m_entry4->attributes()->set("CUSTOM", "Attribute", false);
99
m_entry4->attributes()->set("CustomAttrFirst", "AttrValueFirst", false);
100
m_entry4->attributes()->set("CustomAttrSecond", "AttrValueSecond", false);
101
m_entry4->attributes()->set("CustomAttrThird", "AttrValueThird", false);
102
association.window = "//^CustomAttr1$//";
103
association.sequence = "{PASSWORD}:{S:CUSTOM}";
104
m_entry4->autoTypeAssociations()->add(association);
105
association.window = "//^CustomAttr2$//";
106
association.sequence = "{S:CuStOm}";
107
m_entry4->autoTypeAssociations()->add(association);
108
association.window = "//^CustomAttr3$//";
109
association.sequence = "{PaSSworD}";
110
m_entry4->autoTypeAssociations()->add(association);
111
association.window = "//^{S:CustomAttrFirst}$//";
112
association.sequence = "custom_attr_first";
113
m_entry4->autoTypeAssociations()->add(association);
114
association.window = "//{S:CustomAttrFirst}And{S:CustomAttrSecond}//";
115
association.sequence = "custom_attr_first_and_second";
116
m_entry4->autoTypeAssociations()->add(association);
117
association.window = "//{S:CustomAttrThird}//";
118
association.sequence = "custom_attr_third";
119
m_entry4->autoTypeAssociations()->add(association);
121
m_entry5 = new Entry();
122
m_entry5->setGroup(m_group);
123
m_entry5->setPassword("example5");
124
m_entry5->setTitle("some title");
125
m_entry5->setUrl("http://example.org");
128
void TestAutoType::cleanup()
132
void TestAutoType::testInternal()
134
QVERIFY(m_platform->activeWindowTitle().isEmpty());
136
m_test->setActiveWindowTitle("Test");
137
QCOMPARE(m_platform->activeWindowTitle(), QString("Test"));
140
void TestAutoType::testSingleAutoType()
142
m_autoType->performAutoType(m_entry1);
144
QCOMPARE(m_test->actionCount(), 14);
145
QCOMPARE(m_test->actionChars(),
146
QString("myuser%1mypass%2").arg(m_test->keyToString(Qt::Key_Tab)).arg(m_test->keyToString(Qt::Key_Enter)));
149
void TestAutoType::testGlobalAutoTypeWithNoMatch()
151
m_test->setActiveWindowTitle("nomatch");
152
MessageBox::setNextAnswer(MessageBox::Ok);
153
m_autoType->performGlobalAutoType(m_dbList);
155
QCOMPARE(m_test->actionChars(), QString());
158
void TestAutoType::testGlobalAutoTypeWithOneMatch()
160
m_test->setActiveWindowTitle("custom window");
161
emit osUtils->globalShortcutTriggered("autotype");
162
m_autoType->performGlobalAutoType(m_dbList);
164
QCOMPARE(m_test->actionChars(), QString("%1association%2").arg(m_entry1->username()).arg(m_entry1->password()));
167
void TestAutoType::testGlobalAutoTypeTitleMatch()
169
config()->set(Config::AutoTypeEntryTitleMatch, true);
171
m_test->setActiveWindowTitle("An Entry Title!");
172
emit osUtils->globalShortcutTriggered("autotype");
173
m_autoType->performGlobalAutoType(m_dbList);
175
QCOMPARE(m_test->actionChars(), QString("%1%2").arg(m_entry2->password(), m_test->keyToString(Qt::Key_Enter)));
178
void TestAutoType::testGlobalAutoTypeUrlMatch()
180
config()->set(Config::AutoTypeEntryTitleMatch, true);
182
m_test->setActiveWindowTitle("Dummy - http://example.org/ - <My Browser>");
183
emit osUtils->globalShortcutTriggered("autotype");
184
m_autoType->performGlobalAutoType(m_dbList);
186
QCOMPARE(m_test->actionChars(), QString("%1%2").arg(m_entry5->password(), m_test->keyToString(Qt::Key_Enter)));
189
void TestAutoType::testGlobalAutoTypeUrlSubdomainMatch()
191
config()->set(Config::AutoTypeEntryTitleMatch, true);
193
m_test->setActiveWindowTitle("Dummy - http://sub.example.org/ - <My Browser>");
194
emit osUtils->globalShortcutTriggered("autotype");
195
m_autoType->performGlobalAutoType(m_dbList);
197
QCOMPARE(m_test->actionChars(), QString("%1%2").arg(m_entry5->password(), m_test->keyToString(Qt::Key_Enter)));
200
void TestAutoType::testGlobalAutoTypeTitleMatchDisabled()
202
m_test->setActiveWindowTitle("An Entry Title!");
203
emit osUtils->globalShortcutTriggered("autotype");
204
MessageBox::setNextAnswer(MessageBox::Ok);
205
m_autoType->performGlobalAutoType(m_dbList);
207
QCOMPARE(m_test->actionChars(), QString());
210
void TestAutoType::testGlobalAutoTypeRegExp()
213
m_test->setActiveWindowTitle("lorem REGEX1 ipsum");
214
emit osUtils->globalShortcutTriggered("autotype");
215
m_autoType->performGlobalAutoType(m_dbList);
216
QCOMPARE(m_test->actionChars(), QString("regex1"));
217
m_test->clearActions();
220
m_test->setActiveWindowTitle("lorem regex1 ipsum");
221
emit osUtils->globalShortcutTriggered("autotype");
222
m_autoType->performGlobalAutoType(m_dbList);
223
QCOMPARE(m_test->actionChars(), QString("regex1"));
224
m_test->clearActions();
227
m_test->setActiveWindowTitle("REGEX2");
228
emit osUtils->globalShortcutTriggered("autotype");
229
m_autoType->performGlobalAutoType(m_dbList);
230
QCOMPARE(m_test->actionChars(), QString("regex2"));
231
m_test->clearActions();
234
m_test->setActiveWindowTitle("REGEX3-R2D2");
235
emit osUtils->globalShortcutTriggered("autotype");
236
m_autoType->performGlobalAutoType(m_dbList);
237
QCOMPARE(m_test->actionChars(), QString("regex3"));
238
m_test->clearActions();
241
m_test->setActiveWindowTitle("CustomAttr1");
242
emit osUtils->globalShortcutTriggered("autotype");
243
m_autoType->performGlobalAutoType(m_dbList);
244
QCOMPARE(m_test->actionChars(), QString("custom_attr:Attribute"));
245
m_test->clearActions();
248
m_test->setActiveWindowTitle("CustomAttr2");
249
emit osUtils->globalShortcutTriggered("autotype");
250
m_autoType->performGlobalAutoType(m_dbList);
251
QCOMPARE(m_test->actionChars(), QString(""));
252
m_test->clearActions();
255
m_test->setActiveWindowTitle("CustomAttr3");
256
emit osUtils->globalShortcutTriggered("autotype");
257
m_autoType->performGlobalAutoType(m_dbList);
258
QCOMPARE(m_test->actionChars(), QString("custom_attr"));
259
m_test->clearActions();
262
m_test->setActiveWindowTitle("AttrValueFirst");
263
emit osUtils->globalShortcutTriggered("autotype");
264
m_autoType->performGlobalAutoType(m_dbList);
265
QCOMPARE(m_test->actionChars(), QString("custom_attr_first"));
266
m_test->clearActions();
268
m_test->setActiveWindowTitle("lorem AttrValueFirstAndAttrValueSecond ipsum");
269
emit osUtils->globalShortcutTriggered("autotype");
270
m_autoType->performGlobalAutoType(m_dbList);
271
QCOMPARE(m_test->actionChars(), QString("custom_attr_first_and_second"));
272
m_test->clearActions();
274
m_test->setActiveWindowTitle("lorem AttrValueThird ipsum");
275
emit osUtils->globalShortcutTriggered("autotype");
276
m_autoType->performGlobalAutoType(m_dbList);
277
QCOMPARE(m_test->actionChars(), QString("custom_attr_third"));
278
m_test->clearActions();
281
void TestAutoType::testAutoTypeResults()
283
QScopedPointer<Entry> entry(new Entry());
284
entry->setUsername("Username");
285
entry->setPassword("Password@1");
286
entry->setUrl("https://example.com");
287
entry->attributes()->set("attr1", "value1");
288
entry->attributes()->set("attr2", "decode%20me");
290
QFETCH(QString, sequence);
291
QFETCH(QString, expectedResult);
293
m_autoType->performAutoTypeWithSequence(entry.data(), sequence);
294
QCOMPARE(m_test->actionChars(), expectedResult);
297
void TestAutoType::testAutoTypeResults_data()
299
QTest::addColumn<QString>("sequence");
300
QTest::addColumn<QString>("expectedResult");
303
QTest::newRow("Sequence with Attributes") << QString("{USERNAME} {PASSWORD} {URL} {S:attr1}")
304
<< QString("Username Password@1 https://example.com value1");
305
QTest::newRow("Sequence with Comment") << QString("{USERNAME}{TAB}{C:Extra Tab}{TAB}{S:attr1}")
306
<< QString("Username[Key0x1000001][Key0x1000001]value1");
309
QTest::newRow("T-CONV UPPER") << QString("{T-CONV:/{USERNAME}/UPPER/}") << QString("USERNAME");
310
QTest::newRow("T-CONV LOWER") << QString("{T-CONV:/{USERNAME}/LOWER/}") << QString("username");
311
QTest::newRow("T-CONV BASE64") << QString("{T-CONV:/{USERNAME}/BASE64/}") << QString("VXNlcm5hbWU=");
312
QTest::newRow("T-CONV HEX") << QString("{T-CONV:/{USERNAME}/HEX/}") << QString("557365726e616d65");
313
QTest::newRow("T-CONV URI ENCODE") << QString("{T-CONV:/{URL}/URI/}") << QString("https%3A%2F%2Fexample.com");
314
QTest::newRow("T-CONV URI DECODE") << QString("{T-CONV:/{S:attr2}/URI-DEC/}") << QString("decode me");
315
QTest::newRow("T-REPLACE-RX") << QString("{T-REPLACE-RX:/{USERNAME}/(User)/$1Pass/}") << QString("UserPassname");
318
void TestAutoType::testAutoTypeSyntaxChecks()
320
auto entry = new Entry();
324
QVERIFY2(AutoType::verifyAutoTypeSyntax(
325
"{F1 23}{~ 23}{% 23}{^}{F12}{(}{) 23}{[}{[}{]}{Delay=23}{+}{SUBTRACT}~+%@fixedstring", entry, error),
328
QVERIFY2(AutoType::verifyAutoTypeSyntax("{NUMPAD1 3}", entry, error), error.toLatin1());
330
QVERIFY2(AutoType::verifyAutoTypeSyntax("{S:SPECIALTOKEN}", entry, error), error.toLatin1());
331
QVERIFY2(AutoType::verifyAutoTypeSyntax("{S:SPECIAL TOKEN}", entry, error), error.toLatin1());
332
QVERIFY2(AutoType::verifyAutoTypeSyntax("{S:SPECIAL_TOKEN}", entry, error), error.toLatin1());
333
QVERIFY2(AutoType::verifyAutoTypeSyntax("{S:SPECIAL-TOKEN}", entry, error), error.toLatin1());
334
QVERIFY2(AutoType::verifyAutoTypeSyntax("{S:SPECIAL:TOKEN}", entry, error), error.toLatin1());
335
QVERIFY2(AutoType::verifyAutoTypeSyntax("{S:SPECIAL_TOKEN}{ENTER}", entry, error), error.toLatin1());
336
QVERIFY2(AutoType::verifyAutoTypeSyntax("{S:FOO}{S:HELLO WORLD}", entry, error), error.toLatin1());
337
QVERIFY2(!AutoType::verifyAutoTypeSyntax("{S:SPECIAL_TOKEN{}}", entry, error), error.toLatin1());
339
QVERIFY2(AutoType::verifyAutoTypeSyntax("{BEEP 3 3}", entry, error), error.toLatin1());
340
QVERIFY2(AutoType::verifyAutoTypeSyntax("{BEEP 3}", entry, error), error.toLatin1());
342
QVERIFY2(AutoType::verifyAutoTypeSyntax("{VKEY 0x01}", entry, error), error.toLatin1());
343
QVERIFY2(AutoType::verifyAutoTypeSyntax("{VKEY VK_LBUTTON}", entry, error), error.toLatin1());
344
QVERIFY2(AutoType::verifyAutoTypeSyntax("{VKEY-EX 0x01}", entry, error), error.toLatin1());
346
QVERIFY2(!AutoType::verifyAutoTypeSyntax("{{{}}{}{}}{{}}", entry, error), error.toLatin1());
348
QVERIFY2(AutoType::verifyAutoTypeSyntax("{{}{}}{}}{{}", entry, error), error.toLatin1());
349
QVERIFY2(AutoType::verifyAutoTypeSyntax("{]}{[}{[}{]}", entry, error), error.toLatin1());
350
QVERIFY2(AutoType::verifyAutoTypeSyntax("{)}{(}{(}{)}", entry, error), error.toLatin1());
352
QVERIFY2(!AutoType::verifyAutoTypeSyntax("{DELAY 50000}", entry, error), error.toLatin1());
353
QVERIFY2(AutoType::verifyAutoTypeSyntax("{delay 50}", entry, error), error.toLatin1());
355
QVERIFY2(!AutoType::verifyAutoTypeSyntax("{DELAY=50000}", entry, error), error.toLatin1());
356
QVERIFY2(AutoType::verifyAutoTypeSyntax("{delay=50}", entry, error), error.toLatin1());
358
QVERIFY2(!AutoType::verifyAutoTypeSyntax("{LEFT 50000000}", entry, error), error.toLatin1());
359
QVERIFY2(AutoType::verifyAutoTypeSyntax("{SPACE 10}{TAB 3}{RIGHT 50}", entry, error), error.toLatin1());
360
QVERIFY2(AutoType::verifyAutoTypeSyntax("{delay 5000000000}", entry, error), error.toLatin1());
362
QVERIFY2(AutoType::verifyAutoTypeSyntax("{T-CONV:/{USERNAME}/base64/}", entry, error), error.toLatin1());
363
QVERIFY2(!AutoType::verifyAutoTypeSyntax("{T-CONV:/{USERNAME}/junk/}", entry, error), error.toLatin1());
364
QVERIFY2(!AutoType::verifyAutoTypeSyntax("{T-CONV:}", entry, error), error.toLatin1());
365
QVERIFY2(AutoType::verifyAutoTypeSyntax("{T-REPLACE-RX:/{USERNAME}/a/b/}", entry, error), error.toLatin1());
366
QVERIFY2(!AutoType::verifyAutoTypeSyntax("{T-REPLACE-RX:/{USERNAME}/a/}", entry, error), error.toLatin1());
367
QVERIFY2(!AutoType::verifyAutoTypeSyntax("{T-REPLACE-RX:}", entry, error), error.toLatin1());
370
void TestAutoType::testAutoTypeEffectiveSequences()
372
QString defaultSequence("{USERNAME}{TAB}{PASSWORD}{ENTER}");
373
QString sequenceG1("{TEST_GROUP1}");
374
QString sequenceG3("{TEST_GROUP3}");
375
QString sequenceE2("{TEST_ENTRY2}");
376
QString sequenceDisabled("{TEST_DISABLED}");
377
QString sequenceOrphan("{TEST_ORPHAN}");
379
QScopedPointer<Database> db(new Database());
380
QPointer<Group> rootGroup = db->rootGroup();
383
QPointer<Group> group1 = new Group();
384
group1->setParent(rootGroup);
385
group1->setDefaultAutoTypeSequence(sequenceG1);
388
QPointer<Group> group2 = new Group();
389
group2->setParent(group1);
392
QPointer<Group> group3 = new Group();
393
group3->setParent(group1);
394
group3->setAutoTypeEnabled(Group::Disable);
395
group3->setDefaultAutoTypeSequence(sequenceG3);
397
QCOMPARE(rootGroup->defaultAutoTypeSequence(), QString());
398
QCOMPARE(rootGroup->effectiveAutoTypeSequence(), defaultSequence);
399
QCOMPARE(group1->defaultAutoTypeSequence(), sequenceG1);
400
QCOMPARE(group1->effectiveAutoTypeSequence(), sequenceG1);
401
QCOMPARE(group2->defaultAutoTypeSequence(), QString());
402
QCOMPARE(group2->effectiveAutoTypeSequence(), sequenceG1);
403
QCOMPARE(group3->defaultAutoTypeSequence(), sequenceG3);
404
QCOMPARE(group3->effectiveAutoTypeSequence(), QString());
407
QPointer<Entry> entry1 = new Entry();
408
entry1->setGroup(rootGroup);
411
QPointer<Entry> entry2 = new Entry();
412
entry2->setDefaultAutoTypeSequence(sequenceE2);
413
entry2->setGroup(rootGroup);
416
QPointer<Entry> entry3 = new Entry();
417
entry3->setGroup(group2);
420
QPointer<Entry> entry4 = new Entry();
421
entry4->setDefaultAutoTypeSequence(sequenceDisabled);
422
entry4->setGroup(group3);
425
QPointer<Entry> entry5 = new Entry();
426
entry5->setGroup(group2);
427
entry5->setDefaultAutoTypeSequence(sequenceDisabled);
428
entry5->setAutoTypeEnabled(false);
431
QScopedPointer<Entry> entry6(new Entry());
432
entry6->setDefaultAutoTypeSequence(sequenceOrphan);
434
QCOMPARE(entry1->defaultAutoTypeSequence(), QString());
435
QCOMPARE(entry1->effectiveAutoTypeSequence(), defaultSequence);
436
QCOMPARE(entry2->defaultAutoTypeSequence(), sequenceE2);
437
QCOMPARE(entry2->effectiveAutoTypeSequence(), sequenceE2);
438
QCOMPARE(entry3->defaultAutoTypeSequence(), QString());
439
QCOMPARE(entry3->effectiveAutoTypeSequence(), sequenceG1);
440
QCOMPARE(entry4->defaultAutoTypeSequence(), sequenceDisabled);
441
QCOMPARE(entry4->effectiveAutoTypeSequence(), QString());
442
QCOMPARE(entry5->defaultAutoTypeSequence(), sequenceDisabled);
443
QCOMPARE(entry5->effectiveAutoTypeSequence(), QString());
444
QCOMPARE(entry6->defaultAutoTypeSequence(), sequenceOrphan);
445
QCOMPARE(entry6->effectiveAutoTypeSequence(), QString());