From 5e89cc5b03934581e437b88ac53134117fe05eef Mon Sep 17 00:00:00 2001 From: Herbert Poul Date: Wed, 6 May 2020 10:11:53 +0200 Subject: [PATCH] Fixed error handling nil UUIDs in recycle bin. Also, only create recycle bin on demand (when deleting items). --- CHANGELOG.md | 5 +++++ example/pubspec.lock | 41 ++++++++++++++++++++++++++++++++++++--- lib/src/kdbx_dao.dart | 35 +++++++++++++++++++++------------ lib/src/kdbx_object.dart | 8 ++++++++ pubspec.yaml | 2 +- test/kdbx4_test.dart | 26 +++++++++++++++++++++++++ test/keepass2test.kdbx | Bin 0 -> 1838 bytes 7 files changed, 101 insertions(+), 16 deletions(-) create mode 100644 test/keepass2test.kdbx diff --git a/CHANGELOG.md b/CHANGELOG.md index a101742..75a493e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +## 0.3.1 + +- Fixed error handling nil UUIDs in recycle bin. + Also, only create recycle bin on demand (when deleting items). + ## 0.3.0+1 - Minor fixes for kdbx 4.x diff --git a/example/pubspec.lock b/example/pubspec.lock index 9637e5a..2e98f26 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -8,6 +8,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.5.2" + async: + dependency: transitive + description: + name: async + url: "https://pub.dartlang.org" + source: hosted + version: "2.4.1" charcode: dependency: transitive description: @@ -28,7 +35,7 @@ packages: name: collection url: "https://pub.dartlang.org" source: hosted - version: "1.14.11" + version: "1.14.12" convert: dependency: transitive description: @@ -49,7 +56,14 @@ packages: name: crypto url: "https://pub.dartlang.org" source: hosted - version: "2.1.2" + version: "2.1.4" + cryptography: + dependency: transitive + description: + name: cryptography + url: "https://pub.dartlang.org" + source: hosted + version: "0.1.2" dio: dependency: transitive description: @@ -57,6 +71,20 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.1.16" + ffi: + dependency: transitive + description: + name: ffi + url: "https://pub.dartlang.org" + source: hosted + version: "0.1.3" + ffi_helper: + dependency: transitive + description: + name: ffi_helper + url: "https://pub.dartlang.org" + source: hosted + version: "1.4.0" flutter: dependency: "direct main" description: flutter @@ -76,13 +104,20 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "0.3.3" + isolate: + dependency: transitive + description: + name: isolate + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.3" kdbx: dependency: "direct main" description: path: ".." relative: true source: path - version: "0.2.1" + version: "0.3.0" logging: dependency: transitive description: diff --git a/lib/src/kdbx_dao.dart b/lib/src/kdbx_dao.dart index 6c96a2d..1664635 100644 --- a/lib/src/kdbx_dao.dart +++ b/lib/src/kdbx_dao.dart @@ -32,27 +32,38 @@ extension KdbxDao on KdbxFile { return group; } + /// Returns the recycle bin, if it exists, null otherwise. KdbxGroup get recycleBin { final uuid = body.meta.recycleBinUUID.get(); - if (uuid == null) { - return _createRecycleBin(); + if (uuid?.isNil != false) { + return null; } - _logger.finer(() { - final groupDebug = body.rootGroup - .getAllGroups() - .map((g) => '${g.uuid}: ${g.name}') - .join('\n'); - return 'All Groups: $groupDebug'; - }); - return findGroupByUuid(uuid); + try { + return findGroupByUuid(uuid); + } catch (e, stackTrace) { + _logger.warning(() { + final groupDebug = body.rootGroup + .getAllGroups() + .map((g) => '${g.uuid}: ${g.name}') + .join('\n'); + return 'All Groups: $groupDebug'; + }); + _logger.severe('Inconsistency error, uuid $uuid not found in groups.', e, + stackTrace); + rethrow; + } + } + + KdbxGroup getRecycleBinOrCreate() { + return recycleBin ?? _createRecycleBin(); } void deleteGroup(KdbxGroup group) { - move(group, recycleBin); + move(group, getRecycleBinOrCreate()); } void deleteEntry(KdbxEntry entry) { - move(entry, recycleBin); + move(entry, getRecycleBinOrCreate()); } void move(KdbxObject kdbxObject, KdbxGroup toGroup) { diff --git a/lib/src/kdbx_object.dart b/lib/src/kdbx_object.dart index 6585017..f40f692 100644 --- a/lib/src/kdbx_object.dart +++ b/lib/src/kdbx_object.dart @@ -125,6 +125,11 @@ class KdbxUuid { KdbxUuid.random() : this(base64.encode(uuidGenerator.parse(uuidGenerator.v4()))); + /// https://tools.ietf.org/html/rfc4122.html#section-4.1.7 + /// > The nil UUID is special form of UUID that is specified to have all + /// 128 bits set to zero. + static const NIL = KdbxUuid('AAAAAAAAAAAAAAAAAAAAAA=='); + static final Uuid uuidGenerator = Uuid(options: {'grng': UuidUtil.cryptoRNG}); @@ -142,4 +147,7 @@ class KdbxUuid { @override int get hashCode => uuid.hashCode; + + /// Whether this is the [NIL] uuid. + bool get isNil => this == NIL; } diff --git a/pubspec.yaml b/pubspec.yaml index 13318b9..e287702 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: kdbx description: KeepassX format implementation in pure dart. (kdbx 3.x and 4.x support). -version: 0.3.0 +version: 0.3.1 homepage: https://github.com/authpass/kdbx.dart environment: diff --git a/test/kdbx4_test.dart b/test/kdbx4_test.dart index 4555494..b9f76ea 100644 --- a/test/kdbx4_test.dart +++ b/test/kdbx4_test.dart @@ -52,6 +52,17 @@ class Argon2Test extends Argon2Base { Argon2Hash argon2hash; } +Future _readKdbxFile( + String filePath, { + String password = 'asdf', +}) async { + final kdbxFormat = KdbxFormat(Argon2Test()); + final data = await File(filePath).readAsBytes(); + final file = await kdbxFormat.read( + data, Credentials(ProtectedValue.fromString(password))); + return file; +} + void main() { Logger.root.level = Level.ALL; PrintAppender().attachToLogger(Logger.root); @@ -132,6 +143,21 @@ void main() { File('test_output_chacha20.kdbx').writeAsBytesSync(output); }); }); + group('recycle bin test', () { + test('empty recycle bin with "zero" uuid', () async { + final file = await _readKdbxFile('test/keepass2test.kdbx'); + final recycleBin = file.recycleBin; + expect(recycleBin, isNull); + }); + test('check deleting item', () async { + final file = await _readKdbxFile('test/keepass2test.kdbx'); + expect(file.recycleBin, isNull); + final entry = file.body.rootGroup.getAllEntries().first; + file.deleteEntry(entry); + expect(file.recycleBin, isNotNull); + expect(entry.parent, equals(file.recycleBin)); + }); + }); } KdbxEntry _createEntry( diff --git a/test/keepass2test.kdbx b/test/keepass2test.kdbx new file mode 100644 index 0000000000000000000000000000000000000000..d7a370ba977418b79e67678669c70b695842b69b GIT binary patch literal 1838 zcmV+}2hsQg*`k_f`%AR}00RI55CAd3^5(yBLr}h01tDtuTK@wC0096100bZa5u^8; zGCYW$BId&tPX{ByX0%Epp2$7+ui;Z$RPsj8WZ5* zat$Qh`4j}&cRSXW0AAk!t>XU5u;Ph4bI#W8s@f^y4`U1t0vP>k{ zOK?${JuQr9!F;{7;U<}KFnE2^V0gC&G40-nIZ2=2+@t-TT6Tu3=Q|$4JpHkjAK~3!5P#nV0JRCx|`Y6Ss$9E}) zX0?<2FNxiVz?g*Ia5a_fC%OITh-|D_iU`#5}1wf!;>bL4e&FvAmfUK#E=eriO zNBe6JL5*B1*nD5k>FATFks?}V>3>6I-nh@)pOr8|kZ-uoM3YJGK3n{2hFdz9q|(bk zvq%|cBIe8FD)EVu+nZ}LsN`kU@?BEu^p(|Vrw~&PWYe559iVq0>~NU;R)=Cec6*lf z0y%A|8N##X(m1L9;LZ#m(OqPIb~i>=Q53*HjRiA?b{VX8(Nrk$67!3_d7kS6)o~fG zF%JmIUYM|HmJl5S1^{}+0ec~bXGMx6TA2{IXn;-!M`PhV)poj2 zB08->t8V$Ol|5Byl#V`3Qc}UBY=(U02{9gN_+(r0aA_k2bTBb%Ex4+Q^H-{;gpdh+ zvC8wVCT#=V&kWpd)h zu$ydg;d}v}-CQtqV^Sj-Cw#>H)VRrzn;7_QCrR2S@rJV#_T~8M=%Mumz@0we!D zO^IY~*idgb-MQuwY%4OqvRg=Pgor0aTA_&4ZT5&nA0WW#p(_nrKNvtPk89$o|M`E@ zO9v&zxEfqsfN~U7M^Q~9H<>z~Qfpe;TnRIhEB!WOv4NVw8MRjzoBU)RV(CQZT`Tp@ zYo~Rvj*T&0aDk)i)x@!=DQIEo#jQb83>W(dd4x4{e<`b9QOJ?n5^J+ zlmM(BRGCC6`YwnJ4}rFD$$zAFDxG$$?zv2<&_|vey>1r$Ebi8rgnDX^epH^(5L3a= z8Jj0$YpM@f3bdXS>S`?ahb6$t|GTeA^me$32<;S+B0J|=gv>ZrTai5dKr4fE$k{ha z7oyYU=&<6eb%2;9^d#p9YyT$;3kmvCsU3k<(PcKduc}d^0h%&`{c~O<{#w&WXCo=L zo-#A0UTJ4n#eLZ}G^XaGi!#7nOf?wHiEA;0&?uCT(&O^3zUVaaI+}I=Uq^ks{Xw$k zaJ;)p)Fyb3so({8#E5QGRHzJn9d{ML59|r48p^4 zcPFGA4tJMxK6C;wNkqOuq;K@>?2ngSS@aw_M_!ZrGo17oMnNf`(=`_ln~D8r)?k-D zx?gMfU8kX{L4yau(IMN;&4(FnqYzhNml8FS(}Q61`VA8)t}=>LtH-XxE~MzDz-T+0})|G^WJZ!)1A-)onb95+s(upML(= c^{a^oBY0)~W*D`qt!;0Vl3xm5V}7%I*;6Hay#N3J literal 0 HcmV?d00001