dissect.database 1.2.dev6__py3-none-any.whl → 1.2.dev7__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -6,18 +6,18 @@ from typing import TYPE_CHECKING, BinaryIO
6
6
 
7
7
  from dissect.database.ese.ese import ESE
8
8
  from dissect.database.ese.exception import KeyNotFoundError
9
- from dissect.database.ese.ntds.objects import DomainDNS, Object
9
+ from dissect.database.ese.ntds.objects import Object
10
10
  from dissect.database.ese.ntds.pek import PEK
11
11
  from dissect.database.ese.ntds.query import Query
12
12
  from dissect.database.ese.ntds.schema import Schema
13
13
  from dissect.database.ese.ntds.sd import SecurityDescriptor
14
- from dissect.database.ese.ntds.util import DN, SearchFlags, encode_value
14
+ from dissect.database.ese.ntds.util import DN, DatabaseFlags, SearchFlags, encode_value
15
15
 
16
16
  if TYPE_CHECKING:
17
17
  from collections.abc import Iterator
18
18
 
19
19
  from dissect.database.ese.index import Index
20
- from dissect.database.ese.ntds.objects import DMD, NTDSDSA, Top
20
+ from dissect.database.ese.ntds.objects import DMD, NTDSDSA, DomainDNS, Server, Top
21
21
 
22
22
 
23
23
  class Database:
@@ -34,78 +34,91 @@ class Database:
34
34
  self.link = LinkTable(self)
35
35
  self.sd = SecurityDescriptorTable(self)
36
36
 
37
- self.data.schema.load(self)
38
-
39
-
40
- class DataTable:
41
- """Represents the ``datatable`` in the NTDS database."""
42
-
43
- def __init__(self, db: Database):
44
- self.db = db
45
- self.table = self.db.ese.table("datatable")
46
- self.hiddentable = self.db.ese.table("hiddentable")
37
+ self.hiddentable = self.ese.table("hiddentable")
47
38
  self.hiddeninfo = next(self.hiddentable.records(), None)
48
39
 
49
- self.schema = Schema()
50
-
51
- # Cache frequently used and "expensive" methods
52
- self.get = lru_cache(4096)(self.get)
53
- self._make_dn = lru_cache(4096)(self._make_dn)
54
-
55
- def dsa(self) -> NTDSDSA:
56
- """Return the Directory System Agent (DSA) object."""
57
- if not self.hiddeninfo:
58
- raise ValueError("No hiddentable information available")
59
- return self.get(self.hiddeninfo.get("dsa_col"))
60
-
61
- def dmd(self) -> DMD:
62
- """Return the Directory Management Domain (DMD) object, a.k.a. the schema container."""
63
- if not self.hiddeninfo:
64
- raise ValueError("No hiddentable information available")
65
- return self.get(self.dsa().get("dMDLocation", raw=True))
66
-
67
- def root(self) -> Top:
68
- """Return the top-level object in the NTDS database."""
69
- if (root := next(self.children_of(0), None)) is None:
70
- raise ValueError("No root object found")
71
- return root
40
+ self.data.schema.load(self)
72
41
 
73
- def root_domain(self) -> DomainDNS | None:
74
- """Return the root domain object in the NTDS database. For AD LDS, this will return ``None``."""
75
- stack = [self.root()]
76
- while stack:
77
- if (obj := stack.pop()).is_deleted:
78
- continue
42
+ # Clear the cache of the data table to avoid caching results before the schema is loaded
43
+ self.data.get.cache_clear()
44
+ self.data._make_dn.cache_clear()
79
45
 
80
- if isinstance(obj, DomainDNS) and obj.is_head_of_naming_context:
81
- return obj
46
+ @cached_property
47
+ def flags(self) -> DatabaseFlags | None:
48
+ """Return the database flags."""
49
+ if self.hiddeninfo is None:
50
+ return None
82
51
 
83
- stack.extend(obj.children())
52
+ result = DatabaseFlags(0)
53
+ flags = self.hiddeninfo.get("flags_col")
54
+ for idx, member in enumerate(DatabaseFlags.__members__.values()):
55
+ if flags[idx] == ord(b"1"):
56
+ result = member if result is None else result | member
84
57
 
85
- return None
58
+ return result
86
59
 
87
60
  @cached_property
88
61
  def pek(self) -> PEK | None:
89
62
  """Return the PEK."""
90
- if (root_domain := self.root_domain()) is None:
63
+ if (domain := self.domain()) is None:
91
64
  # Maybe this is an AD LDS database
92
- if (root_pek := self.root().get("pekList")) is None:
65
+ if (root_pek := self.data.root().get("pekList")) is None:
93
66
  # It's not
94
67
  return None
95
68
 
96
69
  # Lookup the schema pek and permutate the boot key
97
70
  # https://www.synacktiv.com/publications/using-ntdissector-to-extract-secrets-from-adam-ntds-files
98
- schema_pek = self.lookup(objectClass="dMD").get("pekList")
71
+ schema_pek = self.dmd().get("pekList")
99
72
  boot_key = bytes(
100
73
  [root_pek[i] for i in [2, 4, 25, 9, 7, 27, 5, 11]]
101
74
  + [schema_pek[i] for i in [37, 2, 17, 36, 20, 11, 22, 7]]
102
75
  )
103
76
 
104
77
  # Lookup the actual PEK and unlock it
105
- pek = PEK(self.lookup(objectClass="configuration").get("pekList"))
78
+ pek = PEK(self.dmd().parent().get("pekList"))
106
79
  pek.unlock(boot_key)
107
80
  return pek
108
- return root_domain.pek
81
+
82
+ return domain.pek
83
+
84
+ def dsa(self) -> NTDSDSA:
85
+ """Return the Directory System Agent (DSA) object, a.k.a. the NTDS Settings object."""
86
+ if not self.hiddeninfo:
87
+ raise ValueError("No hiddentable information available")
88
+ return self.data.get(self.hiddeninfo.get("dsa_col"))
89
+
90
+ def dmd(self) -> DMD:
91
+ """Return the Directory Management Domain (DMD) object, a.k.a. the schema container."""
92
+ if not self.hiddeninfo:
93
+ raise ValueError("No hiddentable information available")
94
+ return self.data.get(self.dsa().get("dMDLocation", raw=True))
95
+
96
+ def dc(self) -> Server:
97
+ """Return the Domain Controller (DC) server object that corresponds to this NTDS database."""
98
+ return self.dsa().parent()
99
+
100
+ def domain(self) -> DomainDNS | None:
101
+ """Return the root domain object in the NTDS database. For AD LDS (ADAM), this will return ``None``."""
102
+ return self.dsa().domain()
103
+
104
+
105
+ class DataTable:
106
+ """Represents the ``datatable`` in the NTDS database."""
107
+
108
+ def __init__(self, db: Database):
109
+ self.db = db
110
+ self.table = self.db.ese.table("datatable")
111
+ self.schema = Schema()
112
+
113
+ # Cache frequently used and "expensive" methods
114
+ self.get = lru_cache(4096)(self.get)
115
+ self._make_dn = lru_cache(4096)(self._make_dn)
116
+
117
+ def root(self) -> Top:
118
+ """Return the top-level object in the NTDS database."""
119
+ if (root := next(self.children_of(0), None)) is None:
120
+ raise ValueError("No root object found")
121
+ return root
109
122
 
110
123
  def walk(self) -> Iterator[Object]:
111
124
  """Walk through all objects in the NTDS database."""
@@ -201,8 +214,11 @@ class DataTable:
201
214
  cursor.seek([dnt])
202
215
 
203
216
  record = cursor.record()
204
- while record is not None and record != end:
217
+ while record is not None:
205
218
  yield Object.from_record(self.db, record)
219
+ if record == end:
220
+ break
221
+
206
222
  record = cursor.next()
207
223
 
208
224
  def _make_dn(self, dnt: int) -> DN:
@@ -41,18 +41,18 @@ class NTDS:
41
41
  def __init__(self, fh: BinaryIO):
42
42
  self.db = Database(fh)
43
43
 
44
+ @property
45
+ def pek(self) -> PEK | None:
46
+ """Return the PEK associated with the root domain."""
47
+ return self.db.pek
48
+
44
49
  def root(self) -> Object:
45
50
  """Return the root object of the Active Directory."""
46
51
  return self.db.data.root()
47
52
 
48
- def root_domain(self) -> DomainDNS | None:
53
+ def domain(self) -> DomainDNS | None:
49
54
  """Return the root domain object of the Active Directory."""
50
- return self.db.data.root_domain()
51
-
52
- @property
53
- def pek(self) -> PEK | None:
54
- """Return the PEK associated with the root domain."""
55
- return self.db.data.pek
55
+ return self.db.domain()
56
56
 
57
57
  def walk(self) -> Iterator[Object]:
58
58
  """Walk through all objects in the NTDS database."""
@@ -7,7 +7,7 @@ from dissect.database.ese.ntds.objects.applicationsettings import ApplicationSet
7
7
  if TYPE_CHECKING:
8
8
  from collections.abc import Iterator
9
9
 
10
- from dissect.database.ese.ntds.objects import Object
10
+ from dissect.database.ese.ntds.objects import DomainDNS, MSDSOptionalFeature, Object
11
11
 
12
12
 
13
13
  class NTDSDSA(ApplicationSettings):
@@ -19,6 +19,18 @@ class NTDSDSA(ApplicationSettings):
19
19
 
20
20
  __object_class__ = "nTDSDSA"
21
21
 
22
+ def domain(self) -> DomainDNS | None:
23
+ """Return the domain object associated with this NTDS DSA object, if any."""
24
+ self._assert_local()
25
+
26
+ return next(self.db.link.links(self.dnt, "msDS-HasDomainNCs"), None)
27
+
28
+ def features(self) -> Iterator[MSDSOptionalFeature]:
29
+ """Return the optional features that are enabled on this NTDS DSA object."""
30
+ self._assert_local()
31
+
32
+ yield from self.db.link.links(self.dnt, "msDS-EnabledFeature")
33
+
22
34
  def managed_by(self) -> Iterator[Object]:
23
35
  """Return the objects that manage this NTDS DSA object."""
24
36
  self._assert_local()
@@ -7,7 +7,7 @@ from dissect.database.ese.ntds.objects.top import Top
7
7
  if TYPE_CHECKING:
8
8
  from collections.abc import Iterator
9
9
 
10
- from dissect.database.ese.ntds.objects import Object
10
+ from dissect.database.ese.ntds.objects import Computer, Object
11
11
 
12
12
 
13
13
  class Server(Top):
@@ -19,6 +19,12 @@ class Server(Top):
19
19
 
20
20
  __object_class__ = "server"
21
21
 
22
+ def computer(self) -> Computer | None:
23
+ """Return the computer object associated with this server, if any."""
24
+ self._assert_local()
25
+
26
+ return next(self.db.link.links(self.dnt, "serverReference"), None)
27
+
22
28
  def managed_by(self) -> Iterator[Object]:
23
29
  """Return the objects that manage this server."""
24
30
  self._assert_local()
@@ -124,6 +124,7 @@ class Query:
124
124
  Yields:
125
125
  Records matching any condition in the OR operation.
126
126
  """
127
+ records = list(records) if records is not None else None
127
128
  for child in filter.children:
128
129
  yield from self._process_query(child, records=records)
129
130
 
@@ -186,8 +187,10 @@ def _process_wildcard_tail(index: Index, filter_value: str) -> Iterator[Record]:
186
187
 
187
188
  # Yield all records in range
188
189
  record = cursor.record()
189
- while record is not None and record != end:
190
+ while record is not None:
190
191
  yield record
192
+ if record == end:
193
+ break
191
194
  record = cursor.next()
192
195
 
193
196
 
@@ -202,7 +202,7 @@ class Schema:
202
202
  # This _should_ have all the attribute and class schema entries
203
203
  # We used to perform an index search on objectClass (ATTc0, INDEX_00000000), but apparently
204
204
  # not all databases have this index
205
- dmd = db.data.dmd()
205
+ dmd = db.dmd()
206
206
 
207
207
  # We bootstrapped these earlier
208
208
  attribute_schema = self.lookup_class(name="attributeSchema")
@@ -232,7 +232,7 @@ class Schema:
232
232
  )
233
233
 
234
234
  # Load user-defined OID prefixes
235
- if (prefix_map := db.data.dmd().get("prefixMap")) is not None:
235
+ if (prefix_map := dmd.get("prefixMap")) is not None:
236
236
  self._oid_idx_to_prefix_index.update(parse_prefix_map(prefix_map))
237
237
  # Rebuild the reverse index
238
238
  self._oid_prefix_to_idx_index = {prefix: idx for idx, prefix in self._oid_idx_to_prefix_index.items()}
@@ -1,7 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import struct
4
- from enum import IntEnum, IntFlag
4
+ from enum import Flag, IntEnum, IntFlag, auto
5
5
  from typing import TYPE_CHECKING, Any
6
6
  from uuid import UUID
7
7
 
@@ -18,6 +18,23 @@ if TYPE_CHECKING:
18
18
  from dissect.database.ese.ntds.schema import AttributeEntry
19
19
 
20
20
 
21
+ class DatabaseFlags(Flag):
22
+ """Database flags that are stored in the hiddentable.
23
+
24
+ The flags are weirdly stored as ``1``, ``0`` or ``\x00`` in a byte array.
25
+ To make parsing a bit easier, we use the index of each flag in this class as the character offset in the byte array.
26
+ """
27
+
28
+ AUXCLASS = auto()
29
+ SD_CONVERSION_REQUIRED = auto()
30
+ ROOT_GUID_UPDATED = auto()
31
+ ADAM = auto()
32
+ ASCII_INDICES_REBUILT = auto()
33
+ SHOW_IN_AB_ARRAY_REBUILD = auto()
34
+ UPDATE_NC_TYPE_REQUIRED = auto()
35
+ LINK_QUOTA_USN = auto()
36
+
37
+
21
38
  # https://learn.microsoft.com/en-us/windows/win32/adschema/a-instancetype
22
39
  class InstanceType(IntFlag):
23
40
  HeadOfNamingContext = 0x00000001
@@ -102,10 +119,10 @@ def _pek_decrypt(db: Database, value: bytes) -> bytes:
102
119
  Returns:
103
120
  The decrypted data blob, or the original value if the PEK is locked.
104
121
  """
105
- if db.data.pek is None or not db.data.pek.unlocked:
122
+ if db.pek is None or not db.pek.unlocked:
106
123
  return value
107
124
 
108
- return db.data.pek.decrypt(value)
125
+ return db.pek.decrypt(value)
109
126
 
110
127
 
111
128
  def _decode_supplemental_credentials(db: Database, value: bytes) -> dict[str, bytes] | bytes:
@@ -118,10 +135,10 @@ def _decode_supplemental_credentials(db: Database, value: bytes) -> dict[str, by
118
135
  Returns:
119
136
  A dictionary mapping credential types to their data blobs, or the original value if the PEK is locked.
120
137
  """
121
- if db.data.pek is None or not db.data.pek.unlocked:
138
+ if db.pek is None or not db.pek.unlocked:
122
139
  return value
123
140
 
124
- value = db.data.pek.decrypt(value)
141
+ value = db.pek.decrypt(value)
125
142
  header = c_ds.USER_PROPERTIES_HEADER(value)
126
143
 
127
144
  result = {}
@@ -222,12 +239,12 @@ def _decode_pwd_history(db: Database, value: list[bytes]) -> list[bytes]:
222
239
  Returns:
223
240
  A list of decrypted password hashes, or the original value if the PEK is locked.
224
241
  """
225
- if db.data.pek is None or not db.data.pek.unlocked:
242
+ if db.pek is None or not db.pek.unlocked:
226
243
  return value
227
244
 
228
245
  result = []
229
246
  for buf in value:
230
- buf = db.data.pek.decrypt(buf)
247
+ buf = db.pek.decrypt(buf)
231
248
  # The history attributes can contain multiple hashes concatenated together, so we need to split them up
232
249
  # NT and LM hashes are both 16 bytes long
233
250
  result.extend(buf[i : i + 16] for i in range(0, len(buf), 16))
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dissect.database
3
- Version: 1.2.dev6
3
+ Version: 1.2.dev7
4
4
  Summary: A Dissect module implementing parsers for various database formats, including Berkeley DB, Microsofts Extensible Storage Engine (ESE) and SQLite3
5
5
  Author-email: Dissect Team <dissect@fox-it.com>
6
6
  License-Expression: Apache-2.0
@@ -29,13 +29,13 @@ dissect/database/ese/ntds/c_pek.py,sha256=YU_rRpOEf8uB8OE6bGb25KIGm9e-yrn0tTItRC
29
29
  dissect/database/ese/ntds/c_pek.pyi,sha256=lHfhvHWABT1vLmknu66phOXsylgwzGIWVdbqDIZU5Qw,4683
30
30
  dissect/database/ese/ntds/c_sd.py,sha256=DYICZsWCBzj0OtvhO6vhzzVjK9YP6tzBCsgKgr0pc-k,4294
31
31
  dissect/database/ese/ntds/c_sd.pyi,sha256=7717Y0EBVu37Liu26rqsDWkLIdSCqWn9KK9svtniLqY,5279
32
- dissect/database/ese/ntds/database.py,sha256=rKHdbQObLp9vSV2gquGZ_RBnIY5Y-2hACIe_atTvxbY,16189
33
- dissect/database/ese/ntds/ntds.py,sha256=qvyDsjHs1WyGbDccOtO5XXcvlKH6rkMnaW6KRxd9264,5080
32
+ dissect/database/ese/ntds/database.py,sha256=6T9JGxB0BD9JF9LP4DjWfozm7mPb5eiXP56Q6bGqKnk,16783
33
+ dissect/database/ese/ntds/ntds.py,sha256=ffE_yOvmwDUXwuQSjCDcGagKXx3nUajAkxDr1HpJbHQ,5060
34
34
  dissect/database/ese/ntds/pek.py,sha256=BEmxO175T8QkGVvFQLN9MI9uDCcK4jztuZetbwbbYqU,4154
35
- dissect/database/ese/ntds/query.py,sha256=orTuXH5jXYUVUVL6PtD9rOcdfCKHjOS-D8RzUMEl0sk,7972
36
- dissect/database/ese/ntds/schema.py,sha256=1M0t5qDAcDOdY4MzAE5cLz4klDCTxrxa0Z9hIDFUCvw,16659
35
+ dissect/database/ese/ntds/query.py,sha256=pDLLCVdrCRNq3ripvMDiyxXoFWsVJLwwsRMplRf7C9s,8063
36
+ dissect/database/ese/ntds/schema.py,sha256=xxVL--RRBM8-IP12L86hrz-EEGLqXKAXpQZyOT_Vhn0,16644
37
37
  dissect/database/ese/ntds/sd.py,sha256=Y-oYnJPcLMDB_4X8TLEGtt-n_nC4HLA0WqIS8qYAwAs,5995
38
- dissect/database/ese/ntds/util.py,sha256=mx_b-_mIINR1Mn01rH7bR4CeY0gl0gnkD2gjeMeiAU8,18348
38
+ dissect/database/ese/ntds/util.py,sha256=nuY7NRSlr5V5Jz2NIOEhe3hbzPjFLVQnprXa3swSl_M,18856
39
39
  dissect/database/ese/ntds/objects/__init__.py,sha256=3lI4f0SwILAY4zJRDVtJpe9YaxKspR-1kHXnhpw91e4,10739
40
40
  dissect/database/ese/ntds/objects/applicationsettings.py,sha256=j7UzmF8yxm3LR2lLnmGb7vFvUhYokCUOdhJXnk9xxzE,358
41
41
  dissect/database/ese/ntds/objects/attributeschema.py,sha256=0e_s5N5mnybxuufOEjGIVWlyuJkSgplz9LuIsaS2S38,342
@@ -111,7 +111,7 @@ dissect/database/ese/ntds/objects/mspki_privatekeyrecoveryagent.py,sha256=BA7NKZ
111
111
  dissect/database/ese/ntds/objects/msspp_activationobjectscontainer.py,sha256=l_OpFUHcRZ7LYvXAdfrDXSClX9f78Ca5UJPgNszo3ZE,405
112
112
  dissect/database/ese/ntds/objects/mstpm_informationobjectscontainer.py,sha256=84wqeKMwcShtr6uzgHoG6rMpk_3r22xe6BTvpt24pSg,404
113
113
  dissect/database/ese/ntds/objects/ntdsconnection.py,sha256=mWgzUTQ0o6Gsc6CsyBoakyXV5UejzTlsVp8j3vm10bw,341
114
- dissect/database/ese/ntds/objects/ntdsdsa.py,sha256=HJRbkL8vGeM0sjADYzxsUcfKCram7uR-nbyLejfuPaw,715
114
+ dissect/database/ese/ntds/objects/ntdsdsa.py,sha256=pES4m9UCqxDI6sOQUjyp3Uc0alHN3pNGpr84L46L5ys,1226
115
115
  dissect/database/ese/ntds/objects/ntdsservice.py,sha256=mTdpd2tzm8OOiGm6rJN35_dyeWtvzPYI_rVDBZz0gNc,326
116
116
  dissect/database/ese/ntds/objects/ntdssitesettings.py,sha256=VNlke-Vgxjh0fzd5o2X-4fwCBrkEI1coR4oto5OenPo,709
117
117
  dissect/database/ese/ntds/objects/ntfrssettings.py,sha256=PwIKT6t_zRxfdacWuvzMfq_mIhj_wyIhHzORNcF2eOU,745
@@ -130,7 +130,7 @@ dissect/database/ese/ntds/objects/rrasadministrationdictionary.py,sha256=dzS3xag
130
130
  dissect/database/ese/ntds/objects/samserver.py,sha256=q32pI-Pjd1TOQle_LuR53rjZlDjCY2U2aAYCWjLEaDg,348
131
131
  dissect/database/ese/ntds/objects/secret.py,sha256=0278o0wpy3n5JmhYKFpNDy1MgNcHBbjX0aUYXxeCUlQ,3392
132
132
  dissect/database/ese/ntds/objects/securityobject.py,sha256=hR0a0wjyrLFtSMtRZOwPCYXpfuOCtNMhozK4bkzly-A,330
133
- dissect/database/ese/ntds/objects/server.py,sha256=oCbwfzW7TQm3nSWD4KuWVzhOYnZXFyq6NZ2KuVB2-Wc,652
133
+ dissect/database/ese/ntds/objects/server.py,sha256=9swxb2s6vNTAPsL_mjQBjZNGft-pUvA2-zds1gKfYJ8,889
134
134
  dissect/database/ese/ntds/objects/serverscontainer.py,sha256=NnJ0M23RO-hsKd2vWcvViOMRV8psGJkwiaRC-ghmrk0,345
135
135
  dissect/database/ese/ntds/objects/site.py,sha256=Yj5lLgivDumOaS8QaTdsS0XZfQEkYr7loN9aUwxj_Y4,640
136
136
  dissect/database/ese/ntds/objects/sitelink.py,sha256=cNHis9jsxFPtxfiCfxd4klP495XfjcwrF507wduyADk,313
@@ -158,10 +158,10 @@ dissect/database/sqlite3/encryption/__init__.py,sha256=kJdFWXD9Z_O_QipC-_A9dlVfR
158
158
  dissect/database/sqlite3/encryption/sqlcipher/__init__.py,sha256=kJdFWXD9Z_O_QipC-_A9dlVfR6AOPSOoT8WBhpFbSsE,238
159
159
  dissect/database/sqlite3/encryption/sqlcipher/exception.py,sha256=GKNtzcnAKlWkvjLluruA8LfzCwjRRWubibbH8WM9l2o,121
160
160
  dissect/database/sqlite3/encryption/sqlcipher/sqlcipher.py,sha256=y_oJRKZqoJBeOQaBbniesZKm1sVTFvXZ466rJYZj2xE,11217
161
- dissect_database-1.2.dev6.dist-info/licenses/COPYRIGHT,sha256=pFH-OBYz6Xj23UB0Odz5IhoTR8nsTbJQNlCRV_wMaiE,317
162
- dissect_database-1.2.dev6.dist-info/licenses/LICENSE,sha256=PhUqiw6jAh2KbBdVRPBq_hfAvfcTBin7nZ3CK7NQbTM,11341
163
- dissect_database-1.2.dev6.dist-info/METADATA,sha256=IlCHaoEzTcjjhsI2Y79YVO5BZr2NSAxfhVLgxMBkrzw,5540
164
- dissect_database-1.2.dev6.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
165
- dissect_database-1.2.dev6.dist-info/entry_points.txt,sha256=ZVVKj3Nzjkgm1kBXGWyGNVUJzTbmVgivv9lgFcuLkpk,343
166
- dissect_database-1.2.dev6.dist-info/top_level.txt,sha256=Mn-CQzEYsAbkxrUI0TnplHuXnGVKzxpDw_po_sXpvv4,8
167
- dissect_database-1.2.dev6.dist-info/RECORD,,
161
+ dissect_database-1.2.dev7.dist-info/licenses/COPYRIGHT,sha256=pFH-OBYz6Xj23UB0Odz5IhoTR8nsTbJQNlCRV_wMaiE,317
162
+ dissect_database-1.2.dev7.dist-info/licenses/LICENSE,sha256=PhUqiw6jAh2KbBdVRPBq_hfAvfcTBin7nZ3CK7NQbTM,11341
163
+ dissect_database-1.2.dev7.dist-info/METADATA,sha256=sHAOCCznUzKRwrx21qX2wt81eYmUmZ7_E78gmdUTPJA,5540
164
+ dissect_database-1.2.dev7.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
165
+ dissect_database-1.2.dev7.dist-info/entry_points.txt,sha256=ZVVKj3Nzjkgm1kBXGWyGNVUJzTbmVgivv9lgFcuLkpk,343
166
+ dissect_database-1.2.dev7.dist-info/top_level.txt,sha256=Mn-CQzEYsAbkxrUI0TnplHuXnGVKzxpDw_po_sXpvv4,8
167
+ dissect_database-1.2.dev7.dist-info/RECORD,,