Merge pull request #11965 from geoffw0/realmfix

Swift: Test and fix missing swift/cleartext-storage-database results
This commit is contained in:
Geoffrey White
2023-04-03 14:02:18 +01:00
committed by GitHub
10 changed files with 103 additions and 56 deletions

View File

@@ -3,7 +3,7 @@ private import codeql.swift.elements.decl.NominalTypeDecl
private import codeql.swift.elements.type.Type
class NominalType extends Generated::NominalType {
Type getABaseType() { result = this.getDeclaration().(NominalTypeDecl).getABaseType() }
override Type getABaseType() { result = this.getDeclaration().(NominalTypeDecl).getABaseType() }
NominalType getADerivedType() { result.getABaseType() = this }

View File

@@ -11,4 +11,18 @@ class Type extends Generated::Type {
* ```
*/
Type getUnderlyingType() { result = this }
/**
* Gets any base type of this type. For a `typealias`, this is a base type
* of the aliased type. For example in the following code, both `B` and
* `B_alias` have base type `A`.
* ```
* class A {}
*
* class B : A {}
*
* typealias B_alias = B
* ```
*/
Type getABaseType() { none() }
}

View File

@@ -13,4 +13,6 @@ class TypeAliasType extends Generated::TypeAliasType {
Type getAliasedType() { result = this.getDecl().getAliasedType() }
override Type getUnderlyingType() { result = this.getAliasedType().getUnderlyingType() }
override Type getABaseType() { result = this.getAliasedType().getABaseType() }
}

View File

@@ -49,7 +49,7 @@ private class CoreDataStore extends CleartextStorageDatabaseSink {
// with `coreDataObj.data` is a sink.
// (ideally this would be only members with the `@NSManaged` attribute)
exists(NominalType t, Expr e |
t.getABaseType*().getName() = "NSManagedObject" and
t.getABaseType*().getUnderlyingType().getName() = "NSManagedObject" and
this.(DataFlow::PostUpdateNode).getPreUpdateNode().asExpr() = e and
e.getFullyConverted().getType() = t and
not e.(DeclRefExpr).getDecl() instanceof SelfParamDecl
@@ -67,7 +67,7 @@ private class RealmStore extends CleartextStorageDatabaseSink instanceof DataFlo
// example in `realmObj.data = sensitive` the post-update node corresponding
// with `realmObj.data` is a sink.
exists(NominalType t, Expr e |
t.getABaseType*().getName() = "RealmSwiftObject" and
t.getABaseType*().getUnderlyingType().getName() = "RealmSwiftObject" and
this.getPreUpdateNode().asExpr() = e and
e.getFullyConverted().getType() = t and
not e.(DeclRefExpr).getDecl() instanceof SelfParamDecl

View File

@@ -37,9 +37,10 @@ class CleartextStorageConfig extends TaintTracking::Configuration {
// flow out from fields of an `NSManagedObject` or `RealmSwiftObject` at the sink,
// for example in `realmObj.data = sensitive`.
isSink(node) and
exists(ClassOrStructDecl cd, Decl cx |
cd.getABaseTypeDecl*().getName() = ["NSManagedObject", "RealmSwiftObject"] and
cx.asNominalTypeDecl() = cd and
exists(NominalTypeDecl d, Decl cx |
d.getType().getABaseType*().getUnderlyingType().getName() =
["NSManagedObject", "RealmSwiftObject"] and
cx.asNominalTypeDecl() = d and
c.getAReadContent().(DataFlow::Content::FieldContent).getField() = cx.getAMember()
)
or

View File

@@ -3,11 +3,11 @@
| nominaltype.swift:37:6:37:6 | a_optional_alias | A_optional_alias | A? | getAliasedType:A? |
| nominaltype.swift:38:6:38:6 | b1 | B1 | B1 | getABaseType:A |
| nominaltype.swift:39:6:39:6 | b2 | B2 | B2 | getABaseType:A_alias |
| nominaltype.swift:40:6:40:6 | b1_alias | B1_alias | B1 | getAliasedType:B1 |
| nominaltype.swift:41:6:41:6 | b2_alias | B2_alias | B2 | getAliasedType:B2 |
| nominaltype.swift:40:6:40:6 | b1_alias | B1_alias | B1 | getABaseType:A, getAliasedType:B1 |
| nominaltype.swift:41:6:41:6 | b2_alias | B2_alias | B2 | getABaseType:A_alias, getAliasedType:B2 |
| nominaltype.swift:42:6:42:6 | p | P | P | |
| nominaltype.swift:43:6:43:6 | p_alias | P_alias | P_alias | |
| nominaltype.swift:44:6:44:6 | c1 | C1 | C1 | getABaseType:P |
| nominaltype.swift:45:6:45:6 | c2 | C2 | C2 | getABaseType:P_alias |
| nominaltype.swift:46:6:46:6 | c1_alias | C1_alias | C1 | getAliasedType:C1 |
| nominaltype.swift:47:6:47:6 | c2_alias | C2_alias | C2 | getAliasedType:C2 |
| nominaltype.swift:46:6:46:6 | c1_alias | C1_alias | C1 | getABaseType:P, getAliasedType:C1 |
| nominaltype.swift:47:6:47:6 | c2_alias | C2_alias | C2 | getABaseType:P_alias, getAliasedType:C2 |

View File

@@ -3,7 +3,7 @@ import swift
string describe(Type t) {
result = "getAliasedType:" + t.(TypeAliasType).getAliasedType()
or
result = "getABaseType:" + t.(NominalType).getABaseType()
result = "getABaseType:" + t.getABaseType()
}
from VarDecl v, Type t

View File

@@ -2,6 +2,7 @@ edges
| file://:0:0:0:0 | self [value] : | file://:0:0:0:0 | .value : |
| file://:0:0:0:0 | value : | file://:0:0:0:0 | [post] self [data] : |
| file://:0:0:0:0 | value : | file://:0:0:0:0 | [post] self [notStoredBankAccountNumber] : |
| file://:0:0:0:0 | value : | file://:0:0:0:0 | [post] self [password] : |
| file://:0:0:0:0 | value : | file://:0:0:0:0 | [post] self [value] : |
| testCoreData2.swift:23:13:23:13 | value : | file://:0:0:0:0 | value : |
| testCoreData2.swift:37:2:37:2 | [post] obj [myValue] : | testCoreData2.swift:37:2:37:2 | [post] obj |
@@ -168,30 +169,36 @@ edges
| testGRDB.swift:208:81:208:81 | password : | testGRDB.swift:208:80:208:89 | [...] |
| testGRDB.swift:210:85:210:85 | password : | testGRDB.swift:210:84:210:93 | [...] |
| testGRDB.swift:212:99:212:99 | password : | testGRDB.swift:212:98:212:107 | [...] |
| testRealm.swift:16:6:16:6 | value : | file://:0:0:0:0 | value : |
| testRealm.swift:34:2:34:2 | [post] a [data] : | testRealm.swift:34:2:34:2 | [post] a |
| testRealm.swift:34:11:34:11 | myPassword : | testRealm.swift:16:6:16:6 | value : |
| testRealm.swift:34:11:34:11 | myPassword : | testRealm.swift:34:2:34:2 | [post] a [data] : |
| testRealm.swift:42:2:42:2 | [post] c [data] : | testRealm.swift:42:2:42:2 | [post] c |
| testRealm.swift:42:11:42:11 | myPassword : | testRealm.swift:16:6:16:6 | value : |
| testRealm.swift:42:11:42:11 | myPassword : | testRealm.swift:42:2:42:2 | [post] c [data] : |
| testRealm.swift:52:2:52:3 | [post] ...! [data] : | testRealm.swift:52:2:52:3 | [post] ...! |
| testRealm.swift:52:12:52:12 | myPassword : | testRealm.swift:16:6:16:6 | value : |
| testRealm.swift:52:12:52:12 | myPassword : | testRealm.swift:52:2:52:3 | [post] ...! [data] : |
| testRealm.swift:59:2:59:2 | [post] g [data] : | testRealm.swift:59:2:59:2 | [post] g |
| testRealm.swift:59:11:59:11 | myPassword : | testRealm.swift:16:6:16:6 | value : |
| testRealm.swift:59:11:59:11 | myPassword : | testRealm.swift:59:2:59:2 | [post] g [data] : |
| testRealm.swift:27:6:27:6 | value : | file://:0:0:0:0 | value : |
| testRealm.swift:34:6:34:6 | value : | file://:0:0:0:0 | value : |
| testRealm.swift:41:2:41:2 | [post] a [data] : | testRealm.swift:41:2:41:2 | [post] a |
| testRealm.swift:41:11:41:11 | myPassword : | testRealm.swift:27:6:27:6 | value : |
| testRealm.swift:41:11:41:11 | myPassword : | testRealm.swift:41:2:41:2 | [post] a [data] : |
| testRealm.swift:49:2:49:2 | [post] c [data] : | testRealm.swift:49:2:49:2 | [post] c |
| testRealm.swift:49:11:49:11 | myPassword : | testRealm.swift:27:6:27:6 | value : |
| testRealm.swift:49:11:49:11 | myPassword : | testRealm.swift:49:2:49:2 | [post] c [data] : |
| testRealm.swift:59:2:59:3 | [post] ...! [data] : | testRealm.swift:59:2:59:3 | [post] ...! |
| testRealm.swift:59:12:59:12 | myPassword : | testRealm.swift:27:6:27:6 | value : |
| testRealm.swift:59:12:59:12 | myPassword : | testRealm.swift:59:2:59:3 | [post] ...! [data] : |
| testRealm.swift:66:2:66:2 | [post] g [data] : | testRealm.swift:66:2:66:2 | [post] g |
| testRealm.swift:66:11:66:11 | myPassword : | testRealm.swift:27:6:27:6 | value : |
| testRealm.swift:66:11:66:11 | myPassword : | testRealm.swift:66:2:66:2 | [post] g [data] : |
| testRealm.swift:73:2:73:2 | [post] h [password] : | testRealm.swift:73:2:73:2 | [post] h |
| testRealm.swift:73:15:73:15 | myPassword : | testRealm.swift:34:6:34:6 | value : |
| testRealm.swift:73:15:73:15 | myPassword : | testRealm.swift:73:2:73:2 | [post] h [password] : |
nodes
| file://:0:0:0:0 | .value2 : | semmle.label | .value2 : |
| file://:0:0:0:0 | .value : | semmle.label | .value : |
| file://:0:0:0:0 | .value : | semmle.label | .value : |
| file://:0:0:0:0 | [post] self [data] : | semmle.label | [post] self [data] : |
| file://:0:0:0:0 | [post] self [notStoredBankAccountNumber] : | semmle.label | [post] self [notStoredBankAccountNumber] : |
| file://:0:0:0:0 | [post] self [password] : | semmle.label | [post] self [password] : |
| file://:0:0:0:0 | [post] self [value] : | semmle.label | [post] self [value] : |
| file://:0:0:0:0 | self [value] : | semmle.label | self [value] : |
| file://:0:0:0:0 | value : | semmle.label | value : |
| file://:0:0:0:0 | value : | semmle.label | value : |
| file://:0:0:0:0 | value : | semmle.label | value : |
| file://:0:0:0:0 | value : | semmle.label | value : |
| testCoreData2.swift:23:13:23:13 | value : | semmle.label | value : |
| testCoreData2.swift:37:2:37:2 | [post] obj | semmle.label | [post] obj |
| testCoreData2.swift:37:2:37:2 | [post] obj [myValue] : | semmle.label | [post] obj [myValue] : |
@@ -421,19 +428,23 @@ nodes
| testGRDB.swift:210:85:210:85 | password : | semmle.label | password : |
| testGRDB.swift:212:98:212:107 | [...] | semmle.label | [...] |
| testGRDB.swift:212:99:212:99 | password : | semmle.label | password : |
| testRealm.swift:16:6:16:6 | value : | semmle.label | value : |
| testRealm.swift:34:2:34:2 | [post] a | semmle.label | [post] a |
| testRealm.swift:34:2:34:2 | [post] a [data] : | semmle.label | [post] a [data] : |
| testRealm.swift:34:11:34:11 | myPassword : | semmle.label | myPassword : |
| testRealm.swift:42:2:42:2 | [post] c | semmle.label | [post] c |
| testRealm.swift:42:2:42:2 | [post] c [data] : | semmle.label | [post] c [data] : |
| testRealm.swift:42:11:42:11 | myPassword : | semmle.label | myPassword : |
| testRealm.swift:52:2:52:3 | [post] ...! | semmle.label | [post] ...! |
| testRealm.swift:52:2:52:3 | [post] ...! [data] : | semmle.label | [post] ...! [data] : |
| testRealm.swift:52:12:52:12 | myPassword : | semmle.label | myPassword : |
| testRealm.swift:59:2:59:2 | [post] g | semmle.label | [post] g |
| testRealm.swift:59:2:59:2 | [post] g [data] : | semmle.label | [post] g [data] : |
| testRealm.swift:59:11:59:11 | myPassword : | semmle.label | myPassword : |
| testRealm.swift:27:6:27:6 | value : | semmle.label | value : |
| testRealm.swift:34:6:34:6 | value : | semmle.label | value : |
| testRealm.swift:41:2:41:2 | [post] a | semmle.label | [post] a |
| testRealm.swift:41:2:41:2 | [post] a [data] : | semmle.label | [post] a [data] : |
| testRealm.swift:41:11:41:11 | myPassword : | semmle.label | myPassword : |
| testRealm.swift:49:2:49:2 | [post] c | semmle.label | [post] c |
| testRealm.swift:49:2:49:2 | [post] c [data] : | semmle.label | [post] c [data] : |
| testRealm.swift:49:11:49:11 | myPassword : | semmle.label | myPassword : |
| testRealm.swift:59:2:59:3 | [post] ...! | semmle.label | [post] ...! |
| testRealm.swift:59:2:59:3 | [post] ...! [data] : | semmle.label | [post] ...! [data] : |
| testRealm.swift:59:12:59:12 | myPassword : | semmle.label | myPassword : |
| testRealm.swift:66:2:66:2 | [post] g | semmle.label | [post] g |
| testRealm.swift:66:2:66:2 | [post] g [data] : | semmle.label | [post] g [data] : |
| testRealm.swift:66:11:66:11 | myPassword : | semmle.label | myPassword : |
| testRealm.swift:73:2:73:2 | [post] h | semmle.label | [post] h |
| testRealm.swift:73:2:73:2 | [post] h [password] : | semmle.label | [post] h [password] : |
| testRealm.swift:73:15:73:15 | myPassword : | semmle.label | myPassword : |
subpaths
| testCoreData2.swift:43:35:43:35 | bankAccountNo : | testCoreData2.swift:23:13:23:13 | value : | file://:0:0:0:0 | [post] self [notStoredBankAccountNumber] : | testCoreData2.swift:43:2:43:2 | [post] obj [notStoredBankAccountNumber] : |
| testCoreData2.swift:52:41:52:41 | bankAccountNo : | testCoreData2.swift:23:13:23:13 | value : | file://:0:0:0:0 | [post] self [notStoredBankAccountNumber] : | testCoreData2.swift:52:2:52:10 | [post] ...? [notStoredBankAccountNumber] : |
@@ -449,10 +460,11 @@ subpaths
| testCoreData2.swift:98:18:98:18 | d [value] : | testCoreData2.swift:70:9:70:9 | self [value] : | file://:0:0:0:0 | .value : | testCoreData2.swift:98:18:98:20 | .value : |
| testCoreData2.swift:104:18:104:18 | e : | testCoreData2.swift:70:9:70:9 | self : | file://:0:0:0:0 | .value : | testCoreData2.swift:104:18:104:20 | .value : |
| testCoreData2.swift:105:18:105:18 | e : | testCoreData2.swift:71:9:71:9 | self : | file://:0:0:0:0 | .value2 : | testCoreData2.swift:105:18:105:20 | .value2 : |
| testRealm.swift:34:11:34:11 | myPassword : | testRealm.swift:16:6:16:6 | value : | file://:0:0:0:0 | [post] self [data] : | testRealm.swift:34:2:34:2 | [post] a [data] : |
| testRealm.swift:42:11:42:11 | myPassword : | testRealm.swift:16:6:16:6 | value : | file://:0:0:0:0 | [post] self [data] : | testRealm.swift:42:2:42:2 | [post] c [data] : |
| testRealm.swift:52:12:52:12 | myPassword : | testRealm.swift:16:6:16:6 | value : | file://:0:0:0:0 | [post] self [data] : | testRealm.swift:52:2:52:3 | [post] ...! [data] : |
| testRealm.swift:59:11:59:11 | myPassword : | testRealm.swift:16:6:16:6 | value : | file://:0:0:0:0 | [post] self [data] : | testRealm.swift:59:2:59:2 | [post] g [data] : |
| testRealm.swift:41:11:41:11 | myPassword : | testRealm.swift:27:6:27:6 | value : | file://:0:0:0:0 | [post] self [data] : | testRealm.swift:41:2:41:2 | [post] a [data] : |
| testRealm.swift:49:11:49:11 | myPassword : | testRealm.swift:27:6:27:6 | value : | file://:0:0:0:0 | [post] self [data] : | testRealm.swift:49:2:49:2 | [post] c [data] : |
| testRealm.swift:59:12:59:12 | myPassword : | testRealm.swift:27:6:27:6 | value : | file://:0:0:0:0 | [post] self [data] : | testRealm.swift:59:2:59:3 | [post] ...! [data] : |
| testRealm.swift:66:11:66:11 | myPassword : | testRealm.swift:27:6:27:6 | value : | file://:0:0:0:0 | [post] self [data] : | testRealm.swift:66:2:66:2 | [post] g [data] : |
| testRealm.swift:73:15:73:15 | myPassword : | testRealm.swift:34:6:34:6 | value : | file://:0:0:0:0 | [post] self [password] : | testRealm.swift:73:2:73:2 | [post] h [password] : |
#select
| testCoreData2.swift:37:2:37:2 | obj | testCoreData2.swift:37:16:37:16 | bankAccountNo : | testCoreData2.swift:37:2:37:2 | [post] obj | This operation stores 'obj' in a database. It may contain unencrypted sensitive data from $@. | testCoreData2.swift:37:16:37:16 | bankAccountNo : | bankAccountNo |
| testCoreData2.swift:39:2:39:2 | obj | testCoreData2.swift:39:28:39:28 | bankAccountNo : | testCoreData2.swift:39:2:39:2 | [post] obj | This operation stores 'obj' in a database. It may contain unencrypted sensitive data from $@. | testCoreData2.swift:39:28:39:28 | bankAccountNo : | bankAccountNo |
@@ -542,7 +554,8 @@ subpaths
| testGRDB.swift:208:80:208:89 | [...] | testGRDB.swift:208:81:208:81 | password : | testGRDB.swift:208:80:208:89 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | testGRDB.swift:208:81:208:81 | password : | password |
| testGRDB.swift:210:84:210:93 | [...] | testGRDB.swift:210:85:210:85 | password : | testGRDB.swift:210:84:210:93 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | testGRDB.swift:210:85:210:85 | password : | password |
| testGRDB.swift:212:98:212:107 | [...] | testGRDB.swift:212:99:212:99 | password : | testGRDB.swift:212:98:212:107 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | testGRDB.swift:212:99:212:99 | password : | password |
| testRealm.swift:34:2:34:2 | a | testRealm.swift:34:11:34:11 | myPassword : | testRealm.swift:34:2:34:2 | [post] a | This operation stores 'a' in a database. It may contain unencrypted sensitive data from $@. | testRealm.swift:34:11:34:11 | myPassword : | myPassword |
| testRealm.swift:42:2:42:2 | c | testRealm.swift:42:11:42:11 | myPassword : | testRealm.swift:42:2:42:2 | [post] c | This operation stores 'c' in a database. It may contain unencrypted sensitive data from $@. | testRealm.swift:42:11:42:11 | myPassword : | myPassword |
| testRealm.swift:52:2:52:3 | ...! | testRealm.swift:52:12:52:12 | myPassword : | testRealm.swift:52:2:52:3 | [post] ...! | This operation stores '...!' in a database. It may contain unencrypted sensitive data from $@. | testRealm.swift:52:12:52:12 | myPassword : | myPassword |
| testRealm.swift:59:2:59:2 | g | testRealm.swift:59:11:59:11 | myPassword : | testRealm.swift:59:2:59:2 | [post] g | This operation stores 'g' in a database. It may contain unencrypted sensitive data from $@. | testRealm.swift:59:11:59:11 | myPassword : | myPassword |
| testRealm.swift:41:2:41:2 | a | testRealm.swift:41:11:41:11 | myPassword : | testRealm.swift:41:2:41:2 | [post] a | This operation stores 'a' in a database. It may contain unencrypted sensitive data from $@. | testRealm.swift:41:11:41:11 | myPassword : | myPassword |
| testRealm.swift:49:2:49:2 | c | testRealm.swift:49:11:49:11 | myPassword : | testRealm.swift:49:2:49:2 | [post] c | This operation stores 'c' in a database. It may contain unencrypted sensitive data from $@. | testRealm.swift:49:11:49:11 | myPassword : | myPassword |
| testRealm.swift:59:2:59:3 | ...! | testRealm.swift:59:12:59:12 | myPassword : | testRealm.swift:59:2:59:3 | [post] ...! | This operation stores '...!' in a database. It may contain unencrypted sensitive data from $@. | testRealm.swift:59:12:59:12 | myPassword : | myPassword |
| testRealm.swift:66:2:66:2 | g | testRealm.swift:66:11:66:11 | myPassword : | testRealm.swift:66:2:66:2 | [post] g | This operation stores 'g' in a database. It may contain unencrypted sensitive data from $@. | testRealm.swift:66:11:66:11 | myPassword : | myPassword |
| testRealm.swift:73:2:73:2 | h | testRealm.swift:73:15:73:15 | myPassword : | testRealm.swift:73:2:73:2 | [post] h | This operation stores 'h' in a database. It may contain unencrypted sensitive data from $@. | testRealm.swift:73:15:73:15 | myPassword : | myPassword |

View File

@@ -111,10 +111,13 @@
| testGRDB.swift:208:81:208:81 | password | label:password, type:credential |
| testGRDB.swift:210:85:210:85 | password | label:password, type:credential |
| testGRDB.swift:212:99:212:99 | password | label:password, type:credential |
| testRealm.swift:34:11:34:11 | myPassword | label:myPassword, type:credential |
| testRealm.swift:42:11:42:11 | myPassword | label:myPassword, type:credential |
| testRealm.swift:52:12:52:12 | myPassword | label:myPassword, type:credential |
| testRealm.swift:59:11:59:11 | myPassword | label:myPassword, type:credential |
| testRealm.swift:31:20:31:20 | .password | label:password, type:credential |
| testRealm.swift:41:11:41:11 | myPassword | label:myPassword, type:credential |
| testRealm.swift:49:11:49:11 | myPassword | label:myPassword, type:credential |
| testRealm.swift:59:12:59:12 | myPassword | label:myPassword, type:credential |
| testRealm.swift:66:11:66:11 | myPassword | label:myPassword, type:credential |
| testRealm.swift:73:2:73:4 | .password | label:password, type:credential |
| testRealm.swift:73:15:73:15 | myPassword | label:myPassword, type:credential |
| testSend.swift:29:19:29:19 | passwordPlain | label:passwordPlain, type:credential |
| testSend.swift:33:19:33:19 | passwordPlain | label:passwordPlain, type:credential |
| testSend.swift:45:13:45:13 | password | label:password, type:credential |

View File

@@ -10,12 +10,6 @@ class RealmSwiftObject {
typealias Object = RealmSwiftObject
class MyRealmSwiftObject : RealmSwiftObject {
override init() { data = "" }
var data: String
}
class Realm {
func add(_ object: Object, update: UpdatePolicy = .error) {}
@@ -27,7 +21,20 @@ class Realm {
// --- tests ---
func test1(realm : Realm, myPassword : String, myHashedPassword : String) {
class MyRealmSwiftObject : RealmSwiftObject {
override init() { data = "" }
var data: String
}
class MyRealmSwiftObject2 : Object {
override init() { password = "" }
var username: String?
var password: String?
}
func test1(realm : Realm, myUsername: String, myPassword : String, myHashedPassword : String) {
// add objects (within a transaction) ...
let a = MyRealmSwiftObject()
@@ -58,6 +65,13 @@ func test1(realm : Realm, myPassword : String, myHashedPassword : String) {
g.data = "" // GOOD (not sensitive)
g.data = myPassword // BAD
g.data = "" // GOOD (not sensitive)
// MyRealmSwiftObject2...
let h = MyRealmSwiftObject2()
h.username = myUsername // GOOD (not sensitive)
h.password = myPassword // BAD
realm.add(h)
}
// limitation: its possible to configure a Realm DB to be stored encrypted, if this is done correctly