keepassxc

Форк
0
/
TestAutoType.cpp 
446 строк · 18.2 Кб
1
/*
2
 *  Copyright (C) 2012 Felix Geyer <debfx@fobos.de>
3
 *  Copyright (C) 2017 KeePassXC Team <team@keepassxc.org>
4
 *
5
 *  This program is free software: you can redistribute it and/or modify
6
 *  it under the terms of the GNU General Public License as published by
7
 *  the Free Software Foundation, either version 2 or (at your option)
8
 *  version 3 of the License.
9
 *
10
 *  This program is distributed in the hope that it will be useful,
11
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
 *  GNU General Public License for more details.
14
 *
15
 *  You should have received a copy of the GNU General Public License
16
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
17
 */
18

19
#include "TestAutoType.h"
20

21
#include <QPluginLoader>
22
#include <QTest>
23

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"
33

34
QTEST_GUILESS_MAIN(TestAutoType)
35

36
void TestAutoType::initTestCase()
37
{
38
    QVERIFY(Crypto::init());
39
    Config::createTempFileInstance();
40
    config()->set(Config::AutoTypeDelay, 1);
41
    config()->set(Config::Security_AutoTypeAsk, false);
42
    AutoType::createTestInstance();
43

44
    QPluginLoader loader(resources()->pluginPath("keepassxc-autotype-test"));
45
    loader.setLoadHints(QLibrary::ResolveAllSymbolsHint);
46
    QVERIFY(loader.instance());
47

48
    m_platform = qobject_cast<AutoTypePlatformInterface*>(loader.instance());
49
    QVERIFY(m_platform);
50

51
    m_test = qobject_cast<AutoTypeTestInterface*>(loader.instance());
52
    QVERIFY(m_test);
53

54
    m_autoType = AutoType::instance();
55
}
56

57
void TestAutoType::init()
58
{
59
    config()->set(Config::AutoTypeEntryTitleMatch, false);
60
    m_test->clearActions();
61

62
    m_db = QSharedPointer<Database>::create();
63
    m_dbList.clear();
64
    m_dbList.append(m_db);
65
    m_group = m_db->rootGroup();
66

67
    AutoTypeAssociations::Association association;
68

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);
76

77
    m_entry2 = new Entry();
78
    m_entry2->setGroup(m_group);
79
    m_entry2->setPassword("myuser");
80
    m_entry2->setTitle("entry title");
81

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);
94

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);
120

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");
126
}
127

128
void TestAutoType::cleanup()
129
{
130
}
131

132
void TestAutoType::testInternal()
133
{
134
    QVERIFY(m_platform->activeWindowTitle().isEmpty());
135

136
    m_test->setActiveWindowTitle("Test");
137
    QCOMPARE(m_platform->activeWindowTitle(), QString("Test"));
138
}
139

140
void TestAutoType::testSingleAutoType()
141
{
142
    m_autoType->performAutoType(m_entry1);
143

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)));
147
}
148

149
void TestAutoType::testGlobalAutoTypeWithNoMatch()
150
{
151
    m_test->setActiveWindowTitle("nomatch");
152
    MessageBox::setNextAnswer(MessageBox::Ok);
153
    m_autoType->performGlobalAutoType(m_dbList);
154

155
    QCOMPARE(m_test->actionChars(), QString());
156
}
157

158
void TestAutoType::testGlobalAutoTypeWithOneMatch()
159
{
160
    m_test->setActiveWindowTitle("custom window");
161
    emit osUtils->globalShortcutTriggered("autotype");
162
    m_autoType->performGlobalAutoType(m_dbList);
163

164
    QCOMPARE(m_test->actionChars(), QString("%1association%2").arg(m_entry1->username()).arg(m_entry1->password()));
165
}
166

167
void TestAutoType::testGlobalAutoTypeTitleMatch()
168
{
169
    config()->set(Config::AutoTypeEntryTitleMatch, true);
170

171
    m_test->setActiveWindowTitle("An Entry Title!");
172
    emit osUtils->globalShortcutTriggered("autotype");
173
    m_autoType->performGlobalAutoType(m_dbList);
174

175
    QCOMPARE(m_test->actionChars(), QString("%1%2").arg(m_entry2->password(), m_test->keyToString(Qt::Key_Enter)));
176
}
177

178
void TestAutoType::testGlobalAutoTypeUrlMatch()
179
{
180
    config()->set(Config::AutoTypeEntryTitleMatch, true);
181

182
    m_test->setActiveWindowTitle("Dummy - http://example.org/ - <My Browser>");
183
    emit osUtils->globalShortcutTriggered("autotype");
184
    m_autoType->performGlobalAutoType(m_dbList);
185

186
    QCOMPARE(m_test->actionChars(), QString("%1%2").arg(m_entry5->password(), m_test->keyToString(Qt::Key_Enter)));
187
}
188

189
void TestAutoType::testGlobalAutoTypeUrlSubdomainMatch()
190
{
191
    config()->set(Config::AutoTypeEntryTitleMatch, true);
192

193
    m_test->setActiveWindowTitle("Dummy - http://sub.example.org/ - <My Browser>");
194
    emit osUtils->globalShortcutTriggered("autotype");
195
    m_autoType->performGlobalAutoType(m_dbList);
196

197
    QCOMPARE(m_test->actionChars(), QString("%1%2").arg(m_entry5->password(), m_test->keyToString(Qt::Key_Enter)));
198
}
199

200
void TestAutoType::testGlobalAutoTypeTitleMatchDisabled()
201
{
202
    m_test->setActiveWindowTitle("An Entry Title!");
203
    emit osUtils->globalShortcutTriggered("autotype");
204
    MessageBox::setNextAnswer(MessageBox::Ok);
205
    m_autoType->performGlobalAutoType(m_dbList);
206

207
    QCOMPARE(m_test->actionChars(), QString());
208
}
209

210
void TestAutoType::testGlobalAutoTypeRegExp()
211
{
212
    // substring matches are ok
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();
218

219
    // should be case-insensitive
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();
225

226
    // exact match
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();
232

233
    // a bit more complicated regex
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();
239

240
    // with custom attributes
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();
246

247
    // with (non uppercase) undefined custom attributes
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();
253

254
    // with mixedcase default attributes
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();
260

261
    // with resolve placeholders in window association title
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();
267

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();
273

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();
279
}
280

281
void TestAutoType::testAutoTypeResults()
282
{
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");
289

290
    QFETCH(QString, sequence);
291
    QFETCH(QString, expectedResult);
292

293
    m_autoType->performAutoTypeWithSequence(entry.data(), sequence);
294
    QCOMPARE(m_test->actionChars(), expectedResult);
295
}
296

297
void TestAutoType::testAutoTypeResults_data()
298
{
299
    QTest::addColumn<QString>("sequence");
300
    QTest::addColumn<QString>("expectedResult");
301

302
    // Normal Sequences
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");
307

308
    // Conversions and Replacements
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");
316
}
317

318
void TestAutoType::testAutoTypeSyntaxChecks()
319
{
320
    auto entry = new Entry();
321
    QString error;
322

323
    // Huge sequence
324
    QVERIFY2(AutoType::verifyAutoTypeSyntax(
325
                 "{F1 23}{~ 23}{% 23}{^}{F12}{(}{) 23}{[}{[}{]}{Delay=23}{+}{SUBTRACT}~+%@fixedstring", entry, error),
326
             error.toLatin1());
327

328
    QVERIFY2(AutoType::verifyAutoTypeSyntax("{NUMPAD1 3}", entry, error), error.toLatin1());
329

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());
338

339
    QVERIFY2(AutoType::verifyAutoTypeSyntax("{BEEP 3 3}", entry, error), error.toLatin1());
340
    QVERIFY2(AutoType::verifyAutoTypeSyntax("{BEEP 3}", entry, error), error.toLatin1());
341

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());
345
    // Bad sequence
346
    QVERIFY2(!AutoType::verifyAutoTypeSyntax("{{{}}{}{}}{{}}", entry, error), error.toLatin1());
347
    // Good sequence
348
    QVERIFY2(AutoType::verifyAutoTypeSyntax("{{}{}}{}}{{}", entry, error), error.toLatin1());
349
    QVERIFY2(AutoType::verifyAutoTypeSyntax("{]}{[}{[}{]}", entry, error), error.toLatin1());
350
    QVERIFY2(AutoType::verifyAutoTypeSyntax("{)}{(}{(}{)}", entry, error), error.toLatin1());
351
    // High delay
352
    QVERIFY2(!AutoType::verifyAutoTypeSyntax("{DELAY 50000}", entry, error), error.toLatin1());
353
    QVERIFY2(AutoType::verifyAutoTypeSyntax("{delay 50}", entry, error), error.toLatin1());
354
    // Slow typing
355
    QVERIFY2(!AutoType::verifyAutoTypeSyntax("{DELAY=50000}", entry, error), error.toLatin1());
356
    QVERIFY2(AutoType::verifyAutoTypeSyntax("{delay=50}", entry, error), error.toLatin1());
357
    // Many repetition
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());
361
    // Conversion and Regex
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());
368
}
369

370
void TestAutoType::testAutoTypeEffectiveSequences()
371
{
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}");
378

379
    QScopedPointer<Database> db(new Database());
380
    QPointer<Group> rootGroup = db->rootGroup();
381

382
    // Group with autotype enabled and custom default sequence
383
    QPointer<Group> group1 = new Group();
384
    group1->setParent(rootGroup);
385
    group1->setDefaultAutoTypeSequence(sequenceG1);
386

387
    // Child group with inherit
388
    QPointer<Group> group2 = new Group();
389
    group2->setParent(group1);
390

391
    // Group with autotype disabled and custom default sequence
392
    QPointer<Group> group3 = new Group();
393
    group3->setParent(group1);
394
    group3->setAutoTypeEnabled(Group::Disable);
395
    group3->setDefaultAutoTypeSequence(sequenceG3);
396

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());
405

406
    // Entry from root group
407
    QPointer<Entry> entry1 = new Entry();
408
    entry1->setGroup(rootGroup);
409

410
    // Entry with custom default sequence
411
    QPointer<Entry> entry2 = new Entry();
412
    entry2->setDefaultAutoTypeSequence(sequenceE2);
413
    entry2->setGroup(rootGroup);
414

415
    // Entry from enabled child group
416
    QPointer<Entry> entry3 = new Entry();
417
    entry3->setGroup(group2);
418

419
    // Entry from disabled group
420
    QPointer<Entry> entry4 = new Entry();
421
    entry4->setDefaultAutoTypeSequence(sequenceDisabled);
422
    entry4->setGroup(group3);
423

424
    // Entry from enabled group with disabled autotype
425
    QPointer<Entry> entry5 = new Entry();
426
    entry5->setGroup(group2);
427
    entry5->setDefaultAutoTypeSequence(sequenceDisabled);
428
    entry5->setAutoTypeEnabled(false);
429

430
    // Entry with no parent
431
    QScopedPointer<Entry> entry6(new Entry());
432
    entry6->setDefaultAutoTypeSequence(sequenceOrphan);
433

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());
446
}
447

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.