dissect.target 3.20.dev6__py3-none-any.whl → 3.20.dev8__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.
- dissect/target/helpers/record.py +1 -1
- dissect/target/plugins/apps/container/docker.py +48 -32
- {dissect.target-3.20.dev6.dist-info → dissect.target-3.20.dev8.dist-info}/METADATA +1 -1
- {dissect.target-3.20.dev6.dist-info → dissect.target-3.20.dev8.dist-info}/RECORD +9 -9
- {dissect.target-3.20.dev6.dist-info → dissect.target-3.20.dev8.dist-info}/COPYRIGHT +0 -0
- {dissect.target-3.20.dev6.dist-info → dissect.target-3.20.dev8.dist-info}/LICENSE +0 -0
- {dissect.target-3.20.dev6.dist-info → dissect.target-3.20.dev8.dist-info}/WHEEL +0 -0
- {dissect.target-3.20.dev6.dist-info → dissect.target-3.20.dev8.dist-info}/entry_points.txt +0 -0
- {dissect.target-3.20.dev6.dist-info → dissect.target-3.20.dev8.dist-info}/top_level.txt +0 -0
dissect/target/helpers/record.py
CHANGED
@@ -144,7 +144,6 @@ EmptyRecord = RecordDescriptor(
|
|
144
144
|
)
|
145
145
|
|
146
146
|
COMMON_INTERFACE_ELEMENTS = [
|
147
|
-
("string", "source"),
|
148
147
|
("string", "name"),
|
149
148
|
("string", "type"),
|
150
149
|
("boolean", "enabled"),
|
@@ -152,6 +151,7 @@ COMMON_INTERFACE_ELEMENTS = [
|
|
152
151
|
("net.ipaddress[]", "dns"),
|
153
152
|
("net.ipaddress[]", "ip"),
|
154
153
|
("net.ipaddress[]", "gateway"),
|
154
|
+
("string", "source"),
|
155
155
|
]
|
156
156
|
|
157
157
|
|
@@ -4,7 +4,7 @@ import json
|
|
4
4
|
import logging
|
5
5
|
import re
|
6
6
|
from pathlib import Path
|
7
|
-
from typing import Iterator
|
7
|
+
from typing import Iterator
|
8
8
|
|
9
9
|
from dissect.cstruct import cstruct
|
10
10
|
from dissect.util import ts
|
@@ -128,12 +128,17 @@ class DockerPlugin(Plugin):
|
|
128
128
|
for data_root in self.installs:
|
129
129
|
images_path = data_root.joinpath("image/overlay2/repositories.json")
|
130
130
|
|
131
|
-
if images_path.exists():
|
132
|
-
repositories = json.loads(images_path.read_text()).get("Repositories")
|
133
|
-
else:
|
131
|
+
if not images_path.exists():
|
134
132
|
self.target.log.debug("No docker images found, file %s does not exist.", images_path)
|
135
133
|
continue
|
136
134
|
|
135
|
+
try:
|
136
|
+
repositories = json.loads(images_path.read_text()).get("Repositories", {})
|
137
|
+
except (json.JSONDecodeError, UnicodeDecodeError) as e:
|
138
|
+
self.target.log.warning("Unable to parse JSON in: %s", images_path)
|
139
|
+
self.target.log.debug("", exc_info=e)
|
140
|
+
continue
|
141
|
+
|
137
142
|
for name, tags in repositories.items():
|
138
143
|
for tag, hash in tags.items():
|
139
144
|
image_metadata_path = data_root.joinpath(
|
@@ -142,8 +147,12 @@ class DockerPlugin(Plugin):
|
|
142
147
|
created = None
|
143
148
|
|
144
149
|
if image_metadata_path.exists():
|
145
|
-
|
146
|
-
|
150
|
+
try:
|
151
|
+
image_metadata = json.loads(image_metadata_path.read_text())
|
152
|
+
created = convert_timestamp(image_metadata.get("created"))
|
153
|
+
except (json.JSONDecodeError, UnicodeDecodeError) as e:
|
154
|
+
self.target.log.warning("Unable to parse JSON in: %s", image_metadata_path)
|
155
|
+
self.target.log.debug("", exc_info=e)
|
147
156
|
|
148
157
|
yield DockerImageRecord(
|
149
158
|
name=name,
|
@@ -160,47 +169,51 @@ class DockerPlugin(Plugin):
|
|
160
169
|
|
161
170
|
for data_root in self.installs:
|
162
171
|
for config_path in data_root.joinpath("containers").glob("**/config.v2.json"):
|
163
|
-
|
172
|
+
try:
|
173
|
+
config = json.loads(config_path.read_text())
|
174
|
+
except (json.JSONDecodeError, UnicodeDecodeError) as e:
|
175
|
+
self.target.log.warning("Unable to parse JSON in file: %s", config_path)
|
176
|
+
self.target.log.debug("", exc_info=e)
|
177
|
+
continue
|
178
|
+
|
164
179
|
container_id = config.get("ID")
|
165
180
|
|
166
181
|
# determine state
|
167
|
-
running = config.get("State").get("Running")
|
182
|
+
running = config.get("State", {}).get("Running")
|
168
183
|
if running:
|
169
|
-
ports = config.get("NetworkSettings").get("Ports", {})
|
170
|
-
|
171
|
-
|
172
|
-
ports = config.get("Config").get("ExposedPorts", {})
|
173
|
-
pid = None
|
184
|
+
ports = config.get("NetworkSettings", {}).get("Ports", {})
|
185
|
+
|
186
|
+
if not running or not ports:
|
187
|
+
ports = config.get("Config", {}).get("ExposedPorts", {})
|
174
188
|
|
175
189
|
# parse volumes
|
176
190
|
volumes = []
|
177
|
-
if mount_points := config.get("MountPoints"):
|
178
|
-
for
|
179
|
-
mount_point = mount_points[mp]
|
191
|
+
if mount_points := config.get("MountPoints", {}):
|
192
|
+
for mount_point in mount_points.values():
|
180
193
|
volumes.append(f"{mount_point.get('Source')}:{mount_point.get('Destination')}")
|
181
194
|
|
182
195
|
# determine mount point
|
183
196
|
mount_path = None
|
184
|
-
if config.get("Driver") == "overlay2":
|
197
|
+
if container_id and config.get("Driver") == "overlay2":
|
185
198
|
mount_path = data_root.joinpath("image/overlay2/layerdb/mounts", container_id)
|
186
199
|
if not mount_path.exists():
|
187
|
-
self.target.log.warning("Overlay2 mount path for container %s
|
200
|
+
self.target.log.warning("Overlay2 mount path does not exist for container: %s", container_id)
|
188
201
|
|
189
202
|
else:
|
190
|
-
self.target.log.warning("Encountered unsupported container filesystem %s", config.get("Driver"))
|
203
|
+
self.target.log.warning("Encountered unsupported container filesystem: %s", config.get("Driver"))
|
191
204
|
|
192
205
|
yield DockerContainerRecord(
|
193
206
|
container_id=container_id,
|
194
|
-
image=config.get("Config").get("Image"),
|
195
|
-
image_id=config.get("Image").split(":")[-1],
|
196
|
-
command=config.get(
|
207
|
+
image=config.get("Config", {}).get("Image"),
|
208
|
+
image_id=config.get("Image", "").split(":")[-1],
|
209
|
+
command=f"{config.get('Path', '')} {' '.join(config.get('Args', []))}".strip(),
|
197
210
|
created=convert_timestamp(config.get("Created")),
|
198
211
|
running=running,
|
199
|
-
pid=
|
200
|
-
started=convert_timestamp(config.get("State").get("StartedAt")),
|
201
|
-
finished=convert_timestamp(config.get("State").get("FinishedAt")),
|
212
|
+
pid=config.get("State", {}).get("Pid"),
|
213
|
+
started=convert_timestamp(config.get("State", {}).get("StartedAt")),
|
214
|
+
finished=convert_timestamp(config.get("State", {}).get("FinishedAt")),
|
202
215
|
ports=convert_ports(ports),
|
203
|
-
names=config.get("Name").replace("/", "", 1),
|
216
|
+
names=config.get("Name", "").replace("/", "", 1),
|
204
217
|
volumes=volumes,
|
205
218
|
mount_path=mount_path,
|
206
219
|
config_path=config_path,
|
@@ -288,19 +301,19 @@ class DockerPlugin(Plugin):
|
|
288
301
|
for line in open_decompress(path, "rt"):
|
289
302
|
try:
|
290
303
|
entry = json.loads(line)
|
291
|
-
except json.JSONDecodeError as e:
|
292
|
-
self.target.log.warning("Could not decode JSON line in file %s", path)
|
304
|
+
except (json.JSONDecodeError, UnicodeDecodeError) as e:
|
305
|
+
self.target.log.warning("Could not decode JSON line in file: %s", path)
|
293
306
|
self.target.log.debug("", exc_info=e)
|
294
307
|
continue
|
295
308
|
yield entry
|
296
309
|
|
297
310
|
|
298
|
-
def get_data_path(path: Path) ->
|
311
|
+
def get_data_path(path: Path) -> str | None:
|
299
312
|
"""Returns the configured Docker daemon data-root path."""
|
300
313
|
try:
|
301
314
|
config = json.loads(path.open("rt").read())
|
302
|
-
except json.JSONDecodeError as e:
|
303
|
-
log.warning("Could not read JSON file
|
315
|
+
except (json.JSONDecodeError, UnicodeDecodeError) as e:
|
316
|
+
log.warning("Could not read JSON file: %s", path)
|
304
317
|
log.debug(exc_info=e)
|
305
318
|
|
306
319
|
return config.get("data-root")
|
@@ -341,7 +354,7 @@ def find_installs(target: Target) -> Iterator[Path]:
|
|
341
354
|
yield data_root_path
|
342
355
|
|
343
356
|
|
344
|
-
def convert_timestamp(timestamp: str) -> str:
|
357
|
+
def convert_timestamp(timestamp: str | None) -> str:
|
345
358
|
"""Docker sometimes uses (unpadded) 9 digit nanosecond precision
|
346
359
|
in their timestamp logs, eg. "2022-12-19T13:37:00.123456789Z".
|
347
360
|
|
@@ -350,6 +363,9 @@ def convert_timestamp(timestamp: str) -> str:
|
|
350
363
|
compatbility with the 6 digit %f microsecond directive.
|
351
364
|
"""
|
352
365
|
|
366
|
+
if not timestamp:
|
367
|
+
return
|
368
|
+
|
353
369
|
timestamp_nanoseconds_plus_postfix = timestamp[19:]
|
354
370
|
match = RE_DOCKER_NS.match(timestamp_nanoseconds_plus_postfix)
|
355
371
|
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: dissect.target
|
3
|
-
Version: 3.20.
|
3
|
+
Version: 3.20.dev8
|
4
4
|
Summary: This module ties all other Dissect modules together, it provides a programming API and command line tools which allow easy access to various data sources inside disk images or file collections (a.k.a. targets)
|
5
5
|
Author-email: Dissect Team <dissect@fox-it.com>
|
6
6
|
License: Affero General Public License v3
|
@@ -61,7 +61,7 @@ dissect/target/helpers/mui.py,sha256=i-7XoHbu4WO2fYapK9yGAMW04rFlgRispknc1KQIS5Q
|
|
61
61
|
dissect/target/helpers/network_managers.py,sha256=ByBSe2K3c8hgQC6dokcf-hHdmPcD8PmrOj0xs1C3yhs,25743
|
62
62
|
dissect/target/helpers/polypath.py,sha256=h8p7m_OCNiQljGwoZh5Aflr9H2ot6CZr6WKq1OSw58o,2175
|
63
63
|
dissect/target/helpers/protobuf.py,sha256=b4DsnqrRLrefcDjx7rQno-_LBcwtJXxuKf5RdOegzfE,1537
|
64
|
-
dissect/target/helpers/record.py,sha256=
|
64
|
+
dissect/target/helpers/record.py,sha256=vbsZBUQ5sxsWGaGUJk2yHV5miXvK9HfZgVarM5Cmqto,5852
|
65
65
|
dissect/target/helpers/record_modifier.py,sha256=O_Jj7zOi891HIyAYjxxe6LFPYETHdMa5lNjo4NA_T_w,3969
|
66
66
|
dissect/target/helpers/regutil.py,sha256=kX-sSZbW8Qkg29Dn_9zYbaQrwLumrr4Y8zJ1EhHXIAM,27337
|
67
67
|
dissect/target/helpers/shell_application_ids.py,sha256=hYxrP-YtHK7ZM0ectJFHfoMB8QUXLbYNKmKXMWLZRlA,38132
|
@@ -127,7 +127,7 @@ dissect/target/plugins/apps/browser/edge.py,sha256=tuuIbm4s8nNstA6nIOEfU0LG0jt20
|
|
127
127
|
dissect/target/plugins/apps/browser/firefox.py,sha256=mZBBagFfIdiz9kUyK4Hi989I4g3CWrClBbmpaGMRKxg,32472
|
128
128
|
dissect/target/plugins/apps/browser/iexplore.py,sha256=g_xw0toaiyjevxO8g9XPCOqc-CXZp39FVquRhPFGdTE,8801
|
129
129
|
dissect/target/plugins/apps/container/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
130
|
-
dissect/target/plugins/apps/container/docker.py,sha256=
|
130
|
+
dissect/target/plugins/apps/container/docker.py,sha256=LTsZplaECSfO1Ysp_Y-9WsnNocsreu_iHO8fbSif3g0,16221
|
131
131
|
dissect/target/plugins/apps/remoteaccess/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
132
132
|
dissect/target/plugins/apps/remoteaccess/anydesk.py,sha256=IdijK3F6ppaB_IgKL-xDljlEbb8l9S2U0xSWKqK9xRs,4294
|
133
133
|
dissect/target/plugins/apps/remoteaccess/remoteaccess.py,sha256=DWXkRDVUpFr1icK2fYwSXdZD204Xz0yRuO7rcJOwIwc,825
|
@@ -363,10 +363,10 @@ dissect/target/volumes/luks.py,sha256=OmCMsw6rCUXG1_plnLVLTpsvE1n_6WtoRUGQbpmu1z
|
|
363
363
|
dissect/target/volumes/lvm.py,sha256=wwQVR9I3G9YzmY6UxFsH2Y4MXGBcKL9aayWGCDTiWMU,2269
|
364
364
|
dissect/target/volumes/md.py,sha256=7ShPtusuLGaIv27SvEETtgsuoQyAa4iAAeOR1NEaajI,1689
|
365
365
|
dissect/target/volumes/vmfs.py,sha256=-LoUbn9WNwTtLi_4K34uV_-wDw2W5hgaqxZNj4UmqAQ,1730
|
366
|
-
dissect.target-3.20.
|
367
|
-
dissect.target-3.20.
|
368
|
-
dissect.target-3.20.
|
369
|
-
dissect.target-3.20.
|
370
|
-
dissect.target-3.20.
|
371
|
-
dissect.target-3.20.
|
372
|
-
dissect.target-3.20.
|
366
|
+
dissect.target-3.20.dev8.dist-info/COPYRIGHT,sha256=m-9ih2RVhMiXHI2bf_oNSSgHgkeIvaYRVfKTwFbnJPA,301
|
367
|
+
dissect.target-3.20.dev8.dist-info/LICENSE,sha256=DZak_2itbUtvHzD3E7GNUYSRK6jdOJ-GqncQ2weavLA,34523
|
368
|
+
dissect.target-3.20.dev8.dist-info/METADATA,sha256=VtQsgGmXH9A74YcDFwgv69F90Mk9mrD0-VQTWRKvFQU,12896
|
369
|
+
dissect.target-3.20.dev8.dist-info/WHEEL,sha256=GV9aMThwP_4oNCtvEC2ec3qUYutgWeAzklro_0m4WJQ,91
|
370
|
+
dissect.target-3.20.dev8.dist-info/entry_points.txt,sha256=BWuxAb_6AvUAQpIQOQU0IMTlaF6TDht2AIZK8bHd-zE,492
|
371
|
+
dissect.target-3.20.dev8.dist-info/top_level.txt,sha256=Mn-CQzEYsAbkxrUI0TnplHuXnGVKzxpDw_po_sXpvv4,8
|
372
|
+
dissect.target-3.20.dev8.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|